﻿using BP.DA;
using BP.Difference;
using BP.En;
using BP.Sys;
using BP.Web;
using BP.WF;
using BP.WF.HttpHandler;
using BP.WF.Template;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using System;
using System.Collections;
using System.Data;
using System.Net.Http;
using System.Text;
using BP.WF.DTS;
using System.IO;
using System.Threading.Tasks;
using Ubiety.Dns.Core.Common;
using BP.CCFast.CCMenu;
using BP.Tools;
using NPOI.SS.Formula.Functions;


namespace CCFlow.NetCore.DataUser.API.Controllers
{
    [Route("WF/[controller]/[Action]")]
    [ApiController]
    public class APIController : ControllerBase
    {
        private readonly IHttpClientFactory _httpClientFactory;
        public APIController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }


        /// <summary>
        /// 返回信息格式
        /// </summary>
        /// <param name="code">200=成功，500=异常</param>
        /// <param name="msg"></param>
        /// <param name="data"></param>
        /// <returns></returns>

        protected static object Return_Info(int code, string msg, string data)
        {
            Hashtable ht = new Hashtable();
            ht.Add("code", code); // 200=成功，500=失败.
            ht.Add("message", msg);  //执行信息.
            if (DataType.IsNullOrEmpty(data) == true)
                ht.Add("data", msg);  //一般是json.
            else
                ht.Add("data", data);  //一般是json.

            return ht;
            //string json = "{\"code\":" + code + ",\"msg\":\"" + msg + "\",\"data\":\"" + data + "\"}";
            //return new HttpResponseMessage { Content = new StringContent(json, System.Text.Encoding.GetEncoding("UTF-8"), "application/json") };
        }

        #region 组织结构接口.
        /// <summary>
        /// 检查加密字符串
        /// </summary>
        /// <param name="privateKey">双方约定的加密字符串</param>
        /// <param name="userNo">用户编号</param>
        /// <returns>对比是否成功</returns>
        /// <exception cref="Exception">参数为空的异常</exception>
        private bool CheckPrivateKey(string privateKey, string userNo)
        {
            if (DataType.IsNullOrEmpty(userNo) == true || DataType.IsNullOrEmpty(privateKey) == true)
                throw new Exception("userNo,privateKey 任何一个都不能为空");

            Paras ps = new Paras();
            ps.SQL = "SELECT Pass FROM Port_Emp WHERE No=" + SystemConfig.AppCenterDBVarStr + "No";
            ps.Add("No", userNo);

            string password = DBAccess.RunSQLReturnVal(ps) as string;
            if (DataType.IsNullOrEmpty(password) == true)
                throw new Exception("用户名或者密码错误");

            string str = userNo + DateTime.Now.ToString("yyyy_MM_dd") + password;
            string val = userNo + DBAccess.GenerMD5(str);
            if (val.Equals(privateKey) == false)
                throw new Exception("加密方法对应不上");
            return true;
        }
        /// <summary>
        /// 写入token:
        /// 1.在对方的系统登录后，需要向ccfast写入token.
        /// 2.这个token是在对方的系统登录时候生成的,两个系统都认可这个token.
        /// </summary>
        /// <param name="privateKey">密钥:双方约定的格式</param>
        /// <param name="userNo">要登录的人员</param>
        /// <param name="erpToken">对方系统生成的token</param>
        /// <returns>返回写入成功或者失败</returns>

        [HttpGet, HttpPost]
        public object Port_WriteToken(string privateKey, string userNo, string erpToken)
        {
            try
            {
                //检查key是否符合加密的格式.
                CheckPrivateKey(privateKey, userNo);

                BP.WF.Dev2Interface.Port_Login(userNo);

                BP.WF.Dev2Interface.Port_WriteToken(erpToken);
                return Return_Info(200, "执行成功", "token写入成功");

            }
            catch (Exception ex)
            {
                return Return_Info(500, "写入token失败", ex.Message);
            }
        }


        /**
        * 获取当前节点
        *
        * @param workid 流程实例编号
        * @return 节点
        * @throws Exception 异常信息
        */
    [HttpGet, HttpPost]
    public Object getCurrentNode(long workid, string token) 
    {
        if(DataType.IsNullOrEmpty(token) == true){
            return Return_Info(500,"没有获取到Token","");
        }
        Port_GenerToken(token);
       try
       {
            DataTable dtGWF = DBAccess.RunSQLReturnTable("SELECT WorkID,FK_Node,NodeName,RDT FROM WF_GenerWorkFlow WHERE WorkID=" + workid);
            string fkNode = dtGWF.Rows[0]["FK_Node"].ToString();
            Node node = new Node(fkNode);
            return Return_Info(200,"获取当前节点成功", node.ToJson());
        }catch(Exception e){
            return Return_Info(500,"获取当前节点失败", e.Message);
        }

    }

        /// <summary>
        /// 切换部门-角色
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="deptNo">部门编号</param>
        /// <param name="staNo">角色编号,可以为空.</param>
        /// <returns>执行结果</returns>
        [HttpGet, HttpPost]
        public object Port_Change(string token, string deptNo, string staNo = null)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                //  Int64 workid = Dev2Interface.Port_CheckUserLogin(flowNo, BP.Web.WebUser.No);
                return Return_Info(200, "创建WorkID成功", "ZHING");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "切换部门-角色失败", ex.Message);
            }
        }
        /// <summary>
        /// 系统登陆
        /// </summary>
        /// <param name="privateKey">密钥,默认:userNo+MD5(userNo + DateTime.Now.ToString("yyyy_MM_dd") + password)</param>
        /// <param name="userNo">登陆账号</param>
        /// <param name="orgNo">组织编号</param>
        /// <returns>返回token</returns>
        [HttpGet, HttpPost]
        public object Port_Login(string privateKey, string userNo, string orgNo = null)
        {
            try
            {
                //检查key是否符合加密的格式.
                CheckPrivateKey(privateKey, userNo);

                //执行本地登录.
                BP.WF.Dev2Interface.Port_Login(userNo, orgNo);
                string token = BP.WF.Dev2Interface.Port_GenerToken();

                Hashtable ht = new Hashtable();
                ht.Add("No", WebUser.No);
                ht.Add("Name", WebUser.Name);
                ht.Add("FK_Dept", WebUser.DeptNo);
                ht.Add("FK_DeptName", WebUser.DeptName);

                ht.Add("DeptNo", WebUser.DeptNo);
                ht.Add("DeptName", WebUser.DeptName);

                ht.Add("OrgNo", WebUser.OrgNo);
                ht.Add("OrgName", WebUser.OrgName);
                ht.Add("Token", token);
                // return ReturnMessage();
                return Return_Info(200, "登陆成功", BP.Tools.Json.ToJson(ht));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "登陆失败", ex.Message);
            }
        }
        /// <summary>
        /// 退出登录
        /// </summary>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_LoginOut(string userNo, string orgNo = null)
        {
            try
            {
                Dev2Interface.Port_SigOut();
                return Return_Info(200, "退出成功", null);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "退出失败", ex.Message);
            }
        }
        #endregion 组织结构接口.

        #region 组织人员岗位维护
        /// <summary>
        /// 人员信息保存
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="orgNo"></param>
        /// <param name="userNo"></param>
        /// <param name="userName"></param>
        /// <param name="deptNo"></param>
        /// <param name="kvs"></param>
        /// <param name="stats"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_Emp_Save(string token, string orgNo, string userNo, string userName, string deptNo, string kvs, string stats)
        {
            Port_GenerToken(token);

            if (WebUser.IsAdmin == false)
                return Return_Info(500, "不是管理员不能维护人员信息", null);

            string msg = BP.Port.OrganizationAPI.Port_Emp_Save(orgNo, userNo, userName, deptNo, kvs, stats);

            return Return_Info(200, "执行成功", msg);

        }

        /// <summary>
        /// 人员删除
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="orgNo">组织编号</param>
        /// <param name="userNo">人员编号</param>
        /// <returns>reutrn 1=成功,  其他的标识异常.</returns>
        [HttpGet, HttpPost]
        public object Port_Emp_Delete(string token, string userNo, string orgNo = "")
        {
            Port_GenerToken(token);

            if (WebUser.IsAdmin == false)
                return Return_Info(500, "不是管理员不能删除人员信息", null);

            return Return_Info(200, "删除成功", BP.Port.OrganizationAPI.Port_Emp_Delete(orgNo, userNo));
        }
        /// <returns>return 1 增加成功，其他的增加失败.</returns>

        /// <summary>
        /// 集团模式下同步组织以及管理员信息
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="orgNo">组织编号</param>
        /// <param name="name">组织名称</param>
        /// <param name="adminer">管理员账号</param>
        /// <param name="adminerName">管理员名字</param>
        /// <param name="keyVals">其他的值:@Leaer=zhangsan@Tel=12233333@Idx=1</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_Org_Save(string token, string orgNo, string name, string adminer, string adminerName, string keyVals = "")
        {
            Port_GenerToken(token);

            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能维护组织信息", null);
            return Return_Info(200, "同步成功", BP.Port.OrganizationAPI.Port_Org_Save(orgNo, name, adminer, adminerName, keyVals));

        }

        /// <summary>
        /// 保存部门,如果有此数据则修改,无此数据则增加.
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="no">部门编号</param>
        /// <param name="name">名称</param>
        /// <param name="parentNo">父节点编号</param>
        /// <param name="orgNo">组织编号</param>
        /// <param name="keyVals">其他的值:@Leaer=zhangsan@Tel=18660153393@Idx=1</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_Dept_Save(string token, string no, string name, string parentNo, string orgNo = "", string keyVals = "")
        {
            Port_GenerToken(token);

            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能维护部门信息", null);
            return Return_Info(200, "保存成功", BP.Port.OrganizationAPI.Port_Dept_Save(orgNo, no, name, parentNo, keyVals));

        }
        /// <summary>
        /// 删除部门
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="no">要删除的部门编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_Dept_Delete(string token, string no)
        {
            Port_GenerToken(token);

            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能删除部门信息", null);
            return Return_Info(200, "删除部门成功", BP.Port.OrganizationAPI.Port_Dept_Delete(no));

        }

        /// <summary>
        /// 保存岗位, 如果有此数据则修改，无此数据则增加.
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="orgNo">组织编号</param>
        /// <param name="no">岗位编号</param>
        /// <param name="name">岗位名称</param>
        /// <param name="stationTypeNo">岗位类型编号</param>
        /// <param name="keyVals">其他值</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_Station_Save(string token, string orgNo, string no, string name, string stationTypeNo, string keyVals)
        {
            Port_GenerToken(token);
            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能维护岗位信息", null);
            return Return_Info(200, "保存岗位成功", BP.Port.OrganizationAPI.Port_Station_Save(orgNo, no, name, stationTypeNo, keyVals));
        }
        /// <summary>
        /// 删除岗位.
        /// </summary>
        /// <param name="no">删除指定的岗位编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_Station_Delete(string token, string no)
        {
            //根据token登录
            Port_GenerToken(token);
            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能删除岗位信息", null);
            return Return_Info(200, "删除岗位成功", BP.Port.OrganizationAPI.Port_Station_Delete(no, WebUser.OrgNo));
        }

        /// <summary>
        /// 保存用户组, 如果有此数据则修改，无此数据则增加.
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="orgNo">组织编号</param>
        /// <param name="no">用户组编号</param>
        /// <param name="name">用户组名称</param>
        /// <param name="keyVals">其他值</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_Team_Save(string token, string orgNo, string no, string name, string keyVals)
        {
            Port_GenerToken(token);
            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能维护用户组信息", null);
            return Return_Info(200, "保存用户组成功", BP.Port.OrganizationAPI.Port_Team_Save(orgNo, no, name, keyVals));
        }
        /// <summary>
        /// 删除用户组.
        /// </summary>
        /// <param name="no">删除指定的用户组编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_Team_Delete(string token, string no)
        {
            //根据token登录
            Port_GenerToken(token);
            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能删除用户组信息", null);
            return Return_Info(200, "删除用户组成功", BP.Port.OrganizationAPI.Port_Team_Delete(no));
        }
        /// <summary>
        /// 保存用户组, 如果有此数据则修改，无此数据则增加.
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="orgNo">组织编号</param>
        /// <param name="no">用户组编号</param>
        /// <param name="name">用户组名称</param>
        /// <param name="keyVals">其他值</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_TeamType_Save(string token, string orgNo, string no, string name, string keyVals)
        {
            Port_GenerToken(token);
            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能维护用户组信息", null);
            return Return_Info(200, "保存用户组成功", BP.Port.OrganizationAPI.Port_TeamType_Save(orgNo, no, name, keyVals));
        }
        /// <summary>
        /// 删除用户组.
        /// </summary>
        /// <param name="no">删除指定的用户组编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_TeamType_Delete(string token, string no)
        {
            //根据token登录
            Port_GenerToken(token);
            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能删除用户组信息", null);
            return Return_Info(200, "删除用户组成功", BP.Port.OrganizationAPI.Port_TeamType_Delete(no));
        }

        /// <summary>
        /// 保存岗位类型, 如果有此数据则修改，无此数据则增加.
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="orgNo">组织编号</param>
        /// <param name="no">岗位类型编号</param>
        /// <param name="name">岗位类型名称</param>
        /// <param name="keyVals">其他值</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_StationType_Save(string token, string orgNo, string no, string name, string keyVals)
        {
            Port_GenerToken(token);
            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能维护岗位类型信息", null);
            return Return_Info(200, "保存岗位类型成功", BP.Port.OrganizationAPI.Port_StationType_Save(orgNo, no, name, keyVals));
        }
        /// <summary>
        /// 删除岗位类型.
        /// </summary>
        /// <param name="no">删除指定的岗位类型编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_StationType_Delete(string token, string no)
        {
            //根据token登录
            Port_GenerToken(token);
            if (WebUser.IsAdmin == false)
                return Return_Info(500, "[" + WebUser.Name + "]不是管理员不能删除岗位类型信息", null);
            return Return_Info(200, "删除岗位类型成功", BP.Port.OrganizationAPI.Port_StationType_Delete(no, WebUser.OrgNo));
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="token"></param>
        /// <param name="toEmpNo">发送给人员账号,多个人员用逗号分开.</param>
        /// <param name="title">标题</param>
        /// <param name="message">信息</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Port_SendMessage(string token, string toEmpNo, string title, string message)
        {
            //根据token登录
            Port_GenerToken(token);
            BP.WF.Dev2Interface.Port_SendMsg(toEmpNo, title, message, "SelfMsg");
            return Return_Info(200, "执行成功", "成功发送给:" + toEmpNo);
        }
        #endregion  组织人员岗位维护

        #region 菜单接口.
        /// <summary>
        /// 可以发起的流程
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="domain">流程所属的域</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DB_Start(string token, string domain = "")
        {
            //根据token登录
            Port_GenerToken(token);

            //获取可以发起的列表
            DataTable dt = BP.WF.Dev2Interface.DB_StarFlows(WebUser.No, domain);
            return Return_Info(200, "获取可以发起的流程成功", BP.Tools.Json.ToJson(dt));
        }

        /// <summary>
        /// 所有流程
        /// </summary>
        /// <param name="token">密钥</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DB_AllFlows(string token)
        {
            //根据token登录
            Port_GenerToken(token);

            if (!BP.Web.WebUser.No.ToLower().Equals("admin") == true)
                return Return_Info(500, "不是管理员不能查询所有流程", null);

            //获取所有流程列表
            DataTable dt = BP.WF.Dev2Interface.DB_WorkFlows();
            return Return_Info(200, "获取所有流程成功", BP.Tools.Json.ToJson(dt));
        }
        /// <summary>
        /// 待办
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="domain">系统编号,可以为空.</param>
        /// <param name="flowNo">指定流程的待办，可以为空.</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DB_Todolist(string token, string domain = "", string flowNo = "")
        {
            //根据token登录
            Port_GenerToken(token);
            //获取可以发起的列表
            DataTable dt = BP.WF.Dev2Interface.DB_GenerEmpWorksOfDataTable(WebUser.No, 0, null, domain, flowNo, null);
            return Return_Info(200, "获取待办列表成功", BP.Tools.Json.ToJson(dt));
        }

        /// <summary>
        /// 待办(待阅/已阅)
        /// </summary>
        /// <param name="token"></param>
        /// <param name="isRead">0=待阅，1=已阅</param>
        /// <param name="domain"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public string DB_TodolistByIsRead(string token, string isRead, string domain)
        {
            //根据token登录
            Port_GenerToken(token);
            //获取可以发起的列表
            DataTable dt = BP.WF.Dev2Interface.DB_GenerEmpWorksOfDataTable(WebUser.No, 0, null, domain, null, isRead, null);
            return BP.Tools.Json.ToJson(dt);
        }
        /// <summary>
        /// 在途
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="domain"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DB_Runing(string token, string domain = "")
        {
            //根据token登录
            Port_GenerToken(token);
            //获取可以发起的列表
            DataTable dt = BP.WF.Dev2Interface.DB_GenerRuning(WebUser.No, false, domain);
            return Return_Info(200, "获取在途列表成功", BP.Tools.Json.ToJson(dt));
        }
        /// <summary>
        /// 草稿
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="domain"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DB_Draft(string token, string domain = "")
        {
            //根据token登录
            Port_GenerToken(token);
            //获取可以发起的列表
            DataTable dt = BP.WF.Dev2Interface.DB_GenerDraftDataTable(null, domain);
            return Return_Info(200, "获取草稿成功", BP.Tools.Json.ToJson(dt));
        }

        /// <summary>
        /// 可以发起的流程
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="domain">流程所属的域, 可以为空</param>
        /// <param name="flowNo">指定的流程，可以为空.</param>
        /// <returns>抄送列表</returns>
        [HttpGet, HttpPost]
        public object DB_CCList(string token, string domain = "", string flowNo = "")
        {
            //根据token登录
            Port_GenerToken(token);
            //获取可以发起的列表
            DataTable dt = BP.WF.Dev2Interface.DB_CCList(domain, flowNo);
            return Return_Info(200, "获取抄送成功.", BP.Tools.Json.ToJson(dt));
        }
        /// <summary>
        /// 我参与已完成的
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="domain">流程所属的域, 可以为空</param>
        /// <param name="flowNo">指定的流程，可以为空.</param>
        /// <returns>抄送列表</returns>
        [HttpGet, HttpPost]
        public object DB_Complete(string token, string domain = "", string flowNo = "")
        {
            //根据token登录
            Port_GenerToken(token);
            DataTable dt = BP.WF.Dev2Interface.DB_Complete(domain, flowNo);
            return Return_Info(200, "获取已完成的数据成功.", BP.Tools.Json.ToJson(dt));
        }
        /// <summary>
        /// 打开的表单
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID">工作实例WorkID</param>
        /// <param name="flowNo">流程编号</param>
        /// <param name="nodeID">节点ID</param>
        /// <param name="fid">父WorkID</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object GenerFrmUrl(string token, long workID, string flowNo, int nodeID)
        {
            //根据token登录
            Port_GenerToken(token);

            /*
             * 发起的url需要在该流程的开始节点的表单方案中，使用SDK表单，并把表单的url设置到里面去.
             * 设置步骤:
             * 1. 打开流程设计器.
             * 2. 在开始节点上右键，选择表单方案.
             * 3. 选择SDK表单，把url配置到文本框里去.
             * 比如: /App/F027QingJia.htm
             */
            if (nodeID == 0)
                nodeID = int.Parse(flowNo + "01");

            if (workID == 0)
                workID = BP.WF.Dev2Interface.Node_CreateBlankWork(flowNo, WebUser.No);

            string url = "";
            Node nd = new Node(nodeID);
            if (nd.FormType == NodeFormType.SDKForm || nd.FormType == NodeFormType.SelfForm)
            {
                //.
                url = nd.FormUrl;
                if (url.Contains("?") == true)
                    url += "&FK_Flow=" + flowNo + "&FK_Node=" + nodeID + "&WorkID=" + workID + "&Token=" + token + "&UserNo=" + WebUser.No;
                else
                    url += "?FK_Flow=" + flowNo + "&FK_Node=" + nodeID + "&WorkID=" + workID + "&Token=" + token + "&UserNo=" + WebUser.No;
            }
            else
            {
                url = "/WF/MyFlow.htm?FK_Flow=" + flowNo + "&FK_Node=" + nodeID + "&WorkID=" + workID + "&Token=" + token;
            }
            return Return_Info(200, "获取打开的表单成功", url);
        }
        #endregion 菜单接口.

        #region 节点方法.
        /// <summary>
        /// 根据节点编号获得到达节点的集合
        /// </summary>
        /// <param name="token">Token</param>
        /// <param name="nodeID">节点ID</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_GetNextStepNodesByNodeID(string token, int nodeID)
        {
            //根据token登录
            Port_GenerToken(token);

            try
            {
                //获得可以到达的节点.
                Directions dirs = Dev2Interface.Node_GetNextStepNodesByNodeID(nodeID);
                return Return_Info(200, "获得到达节点的执行成功", dirs.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "获得到达节点的集合失败", ex.Message);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_GetNextstepNodesByworkID(string token, Int32 workID)
        {
            //根据token登录
            Port_GenerToken(token);

            try
            {
                //获得可以到达的节点.
                Directions dirs = Dev2Interface.Node_GetNextStepNodesByWorkID(workID);
                return Return_Info(200, "根据流程实例获得到达节点集合成功", dirs.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "根据流程实例获得到达节点集合失败", ex.Message);
            }
        }
        /// <summary>
        /// 根据流程实例获取到下一个节点的信息
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workID"></param>
        /// <param name="flowNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_GetNextStepNodeByNodeID(string token, Int64 workID, string flowNo)
        {
            if (DataType.IsNullOrEmpty(token) == true)
                return Return_Info(500, "根据流程实例获取到下一个节点的信息失败", "用户的Token值不能为空");
            if (workID == 0)
                return Return_Info(500, "根据流程实例获取到下一个节点的信息失败", "流程实例workID不能为空");
            if (DataType.IsNullOrEmpty(flowNo) == true)
                return Return_Info(500, "根据流程实例获取到下一个节点的信息失败", "流程实例flowNo不能为空");
            //根据token登录
            Port_GenerToken(token);

            try
            {
                //获得可以到达的节点.
               int nodeID = Dev2Interface.Node_GetNextStepNode(flowNo,workID);
                Node nd = new Node(nodeID);
                return Return_Info(200, "根据流程实例获取到下一个节点的信息成功", BP.Tools.Json.ToJson(nd.ToDataTableField()));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "根据流程实例获取到下一个节点的信息失败", ex.Message);
            }
        }
        /// <summary>
        /// 创建WorkID
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="flowNo">流程编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_CreateBlankWorkID(string token, string flowNo)
        {
            //根据token登录
            Port_GenerToken(token);

            try
            {
                long workid = BP.WF.Dev2Interface.Node_CreateBlankWork(flowNo, WebUser.No);
                return Return_Info(200, "创建WorkID成功", workid.ToString());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "创建WorkID失败", ex.Message);
            }
        }
        /// <summary>
        /// 获得流程的信息
        /// </summary>
        /// <param name="token">验证码</param>
        /// <param name="workID">实例ID</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_WorkInfo(string token, long workID)
        {
            //根据token登录
            Port_GenerToken(token);

            DataSet ds = new DataSet();
            try
            {
                //处理主表.
                GenerWorkFlow gwf = new GenerWorkFlow(workID);
                if (gwf.FID != 0)
                {
                    workID = gwf.FID;
                    gwf.WorkID = workID;
                    gwf.Retrieve();
                }

                DataTable dt = gwf.ToDataTableField("WF_GenerWorkFlow");
                dt.Columns.Add("CurrNodeMark"); //增加一个字段.

                DataTable dtMark = Flow_NodesMarkExt(gwf.FlowNo);
                dt.Rows[0]["CurrNodeMark"] = Flow_NodeMarkExt(gwf.NodeID, dtMark);
                ds.Tables.Add(dt); //增加到数据源.

                //处理从表.
                GenerWorkerLists ens = new GenerWorkerLists();
                QueryObject qo = new QueryObject(ens);
                qo.AddWhere("WorkID", workID);
                qo.addOr();
                qo.AddWhere("FID", workID);
                qo.addOrderBy("RDT");
                qo.DoQuery();

                DataTable dtGwls = ens.ToDataTableField("WF_GenerWorkerList");
                dtGwls.Columns.Add("NodeMark"); //增加一个字段.
                //转换NodeI.
                foreach (DataRow item in dtGwls.Rows)
                {
                    int nodeID = int.Parse(item["FK_Node"].ToString());
                    item["NodeMark"] = Flow_NodeMarkExt(nodeID, dtMark);
                }
                ds.Tables.Add(dtGwls); //增加到数据源.

                string info = BP.Tools.Json.ToJson(ds);
                return Return_Info(200, "获取成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "获得流程信息失败", ex.Message);
            }
        }
        /// <summary>
        /// 获取流程轨迹信息
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="flowNo">流程编号</param>
        /// <param name="workID">流程实例编号</param>
        /// <param name="fid">流程实例父流程实例编号，没有传0</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_TrackInfo(string token, string flowNo, long workID, long fid)
        {
            if (DataType.IsNullOrEmpty(token) == true)
            {
                return Return_Info(500, "没有获取到Token", "");
            }
            if (DataType.IsNullOrEmpty(flowNo) == true)
            {
                return Return_Info(500, "没有获取到流程ID", "");
            }
            if (DataType.IsNullOrEmpty(workID.ToString()) == true)
            {
                return Return_Info(500, "没有获取到workID实例ID", "");
            }
            if (DataType.IsNullOrEmpty(fid.ToString()) == true)
            {
                return Return_Info(500, "没有获取到fid，传0", "");
            }
            //根据token登录
            Port_GenerToken(token);

            try
            {
                DataTable dt = Dev2Interface.DB_GenerTrackTable(flowNo, workID, fid);
                return Return_Info(200, "获取流程轨迹信息成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "获取流程轨迹信息失败", ex.Message);
            }
        }

        /// <summary>
        /// 根据WorkID获得流程轨迹.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="flowNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object WorkTrack(string token, long WorkID)
        {
            Port_GenerToken(token);

            DataSet ds = new DataSet();
            try
            {
                GenerWorkFlow gwf = new GenerWorkFlow(WorkID);
                Nodes nds = new Nodes(gwf.FlowNo);
                int curNodeID = gwf.NodeID;
                string checkerPassed = ",";
                string trackTable = "ND" + Int32.Parse(gwf.FlowNo) + "Track";
                NodeWorkCheck wcDesc = new NodeWorkCheck(curNodeID);
                bool isShowCurrNodeInfo = true;
                if (gwf.IsOver)
                {
                    Paras ps = new Paras();
                    ps.SQL = "SELECT FK_Emp FROM WF_GenerWorkerlist WHERE WorkID=" + SystemConfig.AppCenterDBVarStr + "WorkID AND IsPass=1 AND FK_Node=" + SystemConfig.AppCenterDBVarStr + "FK_Node Order By RDT,CDT";
                    ps.Add("WorkID", gwf.WorkID);
                    ps.Add("FK_Node", gwf.NodeID);
                    DataTable checkerPassedDt = DBAccess.RunSQLReturnTable(ps);
                    foreach (DataRow dr in checkerPassedDt.Rows)
                    {
                        checkerPassed += dr[0] + ",";
                    }
                }

                //已经运行过的节点
                string runNodes = "";
                if (gwf.IsOver == false)
                {
                    string sql = "";
                    switch (BP.Difference.SystemConfig.AppCenterDBType)
                    {
                        case DBType.MSSQL:
                            sql = "SELECT DISTINCT(FK_Node) From(SELECT FK_Node From (SELECT ROW_NUMBER() over(ORDER BY RDT) as rn,FK_Node FROM WF_GenerWorkerList WHERE WorkID=" + gwf.WorkID + ") B) A";
                            break;
                        default:
                            sql = "SELECT DISTINCT(FK_Node) From(SELECT FK_Node From WF_GenerWorkerList WHERE WorkID=" + gwf.WorkID + "  ORDER BY RDT) A";
                            break;
                    }
                    DataTable dtt = DBAccess.RunSQLReturnTable(sql);
                    foreach (DataRow dr in dtt.Rows)
                    {
                        runNodes += dr[0].ToString() + ",";
                    }
                }

                bool isCanDo = Dev2Interface.Flow_IsCanDoCurrentWork(gwf.WorkID, WebUser.No);
                DataRow row = null;

                DataTable tkDt = new DataTable("Tracks");
                tkDt.Columns.Add("MyPk", typeof(string));
                //从节点来
                tkDt.Columns.Add("NDFrom", typeof(string));
                //到某节点
                tkDt.Columns.Add("NDTo", typeof(string));
                tkDt.Columns.Add("NDToT", typeof(string));
                tkDt.Columns.Add("NodeID", typeof(int));
                tkDt.Columns.Add("NodeName", typeof(string));
                tkDt.Columns.Add("Msg", typeof(string));
                tkDt.Columns.Add("EmpFrom", typeof(string));
                tkDt.Columns.Add("EmpFromT", typeof(string));
                tkDt.Columns.Add("EmpTo", typeof(string));
                tkDt.Columns.Add("EmpToT", typeof(string));
                tkDt.Columns.Add("DeptName", typeof(string));
                tkDt.Columns.Add("RDT", typeof(string));
                tkDt.Columns.Add("ParentNode", typeof(int));
                tkDt.Columns.Add("ActionType", typeof(int));
                tkDt.Columns.Add("Tag", typeof(string));
                tkDt.Columns.Add("FWCView", typeof(string));
                tkDt.Columns.Add("WritImg", typeof(string));
                tkDt.Columns.Add("WriteStamp", typeof(string));
                tkDt.Columns.Add("SigantureEnabel", typeof(int));
                tkDt.Columns.Add("FWCAth", typeof(int));

                DataTable historyTracks = new DataTable("historyTracks");
                historyTracks.Columns.Add("MyPk", typeof(string));
                //从节点来
                historyTracks.Columns.Add("NDFrom", typeof(string));
                //到某节点
                historyTracks.Columns.Add("NDTo", typeof(string));
                historyTracks.Columns.Add("NDToT", typeof(string));
                historyTracks.Columns.Add("NodeID", typeof(int));
                historyTracks.Columns.Add("NodeName", typeof(string));
                historyTracks.Columns.Add("Msg", typeof(string));
                historyTracks.Columns.Add("EmpFrom", typeof(string));
                historyTracks.Columns.Add("EmpFromT", typeof(string));
                historyTracks.Columns.Add("EmpTo", typeof(string));
                historyTracks.Columns.Add("EmpToT", typeof(string));
                historyTracks.Columns.Add("DeptName", typeof(string));
                historyTracks.Columns.Add("RDT", typeof(string));
                historyTracks.Columns.Add("ParentNode", typeof(int));
                historyTracks.Columns.Add("ActionType", typeof(int));
                historyTracks.Columns.Add("Tag", typeof(string));
                historyTracks.Columns.Add("FWCView", typeof(string));
                historyTracks.Columns.Add("WritImg", typeof(string));
                historyTracks.Columns.Add("WriteStamp", typeof(string));
                historyTracks.Columns.Add("SigantureEnabel", typeof(int));
                historyTracks.Columns.Add("FWCAth", typeof(int));

                WorkCheck wc = null;
                if (gwf.FID != 0)
                    wc = new WorkCheck(gwf.FlowNo, gwf.NodeID, gwf.WorkID, gwf.FID, wcDesc.GetValIntByKey("FWCTimeModel", 0));
                else
                    wc = new WorkCheck(gwf.FlowNo, gwf.NodeID, gwf.WorkID, 0, wcDesc.GetValIntByKey("FWCTimeModel", 0));

                //获取运行过的所有历史审核信息
                string nodes = "";
                Tracks tks = wc.HisWorkChecks;
                Tracks tracks = new Tracks();
                foreach (BP.WF.Track tk in tks)
                {
                    if (tk.HisActionType != ActionType.WorkCheck && tk.HisActionType != ActionType.AutoWorkCheck)
                        tracks.AddEntity(tk);

                    if (tk.HisActionType == ActionType.FlowBBS)
                        continue;

                    if (wcDesc.FWCMsgShow == 1 && tk.EmpFrom.Equals(WebUser.No) == false)
                        continue;

                    switch (tk.HisActionType)
                    {
                        case ActionType.WorkCheck:
                        case ActionType.Forward:
                        case ActionType.StartChildenFlow:
                        case ActionType.ForwardHL:
                        case ActionType.Return:
                        case ActionType.ReturnAndBackWay:
                        case ActionType.CC:
                            if (tk.HisActionType == ActionType.CC && tk.NDFrom != tk.NDTo)
                            {
                                if (nodes.Contains(tk.NDTo + ",") == false)
                                    nodes += tk.NDTo + ",";
                            }
                            if (nodes.Contains(tk.NDFrom + ",") == false)
                                nodes += tk.NDFrom + ",";
                            break;
                        default:
                            continue;
                    }
                }
                int trackIdx = 0;
                foreach (Track tk in tracks)
                {

                    //倒叙
                    if (wc.FWCTimeModel == 0)
                    {
                        if ((trackIdx - 1) >= 0)
                        {
                            Track track = (Track)tracks[trackIdx - 1];
                            if (track.HisActionType == ActionType.UnSend)
                            {
                                trackIdx++;
                                continue;
                            }
                        }
                    }
                    else
                    {
                        if (tracks.Count > (trackIdx + 1))
                        {
                            Track track = (Track)tracks[trackIdx + 1];
                            if (track.HisActionType == ActionType.UnSend)
                            {
                                trackIdx++;
                                continue;
                            }
                        }
                    }

                    trackIdx++;
                    if (nodes.Contains(tk.NDFrom + ",") == false)
                        continue;

                    //退回
                    if (tk.HisActionType == ActionType.Return || tk.HisActionType == ActionType.ReturnAndBackWay)
                    {
                        //1.不显示退回意见 2.显示退回意见但是不是退回给本节点的意见
                        if (wcDesc.FWCIsShowReturnMsg == 0)
                            continue;
                        //zsy5.23修改为无论任何节点都正常展示
                        //if (wcDesc.FWCIsShowReturnMsg == 1 && tk.NDTo != this.NodeID)
                        //    continue;
                    }

                    //  此部分被zhoupeng注释, 在会签的时候显示不到意见。
                    // 如果是当前的节点.当前人员可以处理, 已经审批通过的人员.
                    if (tk.HisActionType != ActionType.Shift && tk.NDFrom == gwf.NodeID
                        && isCanDo == true
                        && tk.EmpFrom.Equals(WebUser.No) == false
                        && checkerPassed.Contains("," + tk.EmpFrom + ",") == false)
                        continue;

                    if (tk.NDFrom == gwf.NodeID && gwf.HuiQianTaskSta != HuiQianTaskSta.None)
                    {
                        //判断会签, 去掉正在审批的节点.
                        if (tk.NDFrom == gwf.NodeID && isShowCurrNodeInfo == false)
                            continue;
                    }

                    //手动抄送也需要展示track信息
                    //if (tk.HisActionType == ActionType.CC && tk.NDFrom == tk.NDTo)
                    //    continue;

                    //历史审核信息现在存放在流程前进的节点中
                    switch (tk.HisActionType)
                    {
                        case ActionType.Forward:
                        case ActionType.ForwardAskfor:
                        case ActionType.Start:
                        //case ActionType.UnSend:
                        // case ActionType.ForwardFL:
                        case ActionType.ForwardHL:
                        case ActionType.SubThreadForward:
                        case ActionType.TeampUp:
                        case ActionType.Return:
                        case ActionType.ReturnAndBackWay:
                        case ActionType.StartChildenFlow:
                        case ActionType.FlowOver:
                        case ActionType.CC:
                        case ActionType.Shift:
                            row = tkDt.NewRow();
                            row["MyPk"] = tk.MyPK;
                            row["NDFrom"] = tk.NDFrom;
                            row["NDTo"] = tk.NDTo;
                            if (tk.HisActionType == ActionType.CC)
                            {

                                row["NodeID"] = tk.NDTo;
                                row["NodeName"] = tk.NDToT;
                                row["EmpFrom"] = tk.EmpTo;
                                row["EmpFromT"] = tk.EmpToT;
                                row["EmpTo"] = tk.EmpTo;
                                row["EmpToT"] = tk.EmpToT;
                            }
                            else
                            {
                                row["NodeID"] = tk.NDFrom;
                                row["NodeName"] = tk.NDFromT;
                                row["EmpFrom"] = tk.EmpFrom;
                                row["EmpFromT"] = tk.EmpFromT;
                                row["EmpTo"] = tk.EmpTo;
                                row["EmpToT"] = tk.EmpToT;
                            }
                            row["ParentNode"] = 0;
                            row["RDT"] = DataType.IsNullOrEmpty(tk.RDT) ? "" : tk.NDFrom == tk.NDTo && DataType.IsNullOrEmpty(tk.Msg) ? "" : tk.RDT;
                            row["Msg"] = tk.MsgHtml;

                            //获取部门
                            string DeptName = "";
                            string[] Arrays = tk.NodeData.Split('@');
                            foreach (string i in Arrays)
                            {
                                if (i.Contains("DeptName="))
                                {
                                    DeptName = i.Split('=')[1];
                                }
                            }
                            row["DeptName"] = DeptName;
                            row["ActionType"] = tk.HisActionType;
                            row["Tag"] = tk.Tag;

                            //获取每个节点的签名方式
                            string sql = "SELECT SigantureEnabel FROM WF_Node WHERE NodeID=" + tk.NDFrom;
                            string sigantureEnabel = DBAccess.RunSQLReturnString(sql);
                            if (DataType.IsNullOrEmpty(sigantureEnabel) == true)
                            {
                                sigantureEnabel = "0";
                            }
                            row["SigantureEnabel"] = sigantureEnabel;

                            //获取每个节点的附件上传
                            sql = "SELECT FWCAth FROM WF_Node WHERE NodeID=" + tk.NDFrom;
                            string fwcAth = DBAccess.RunSQLReturnString(sql);
                            if (DataType.IsNullOrEmpty(fwcAth) == true)
                            {
                                fwcAth = "0";
                            }
                            row["FWCAth"] = fwcAth;

                            row["WritImg"] = "";
                            row["WriteStamp"] = "";

                            //@0=不签名@1=图片签名@2=写字板@3=电子签名@4=电子盖章@5=电子签名+盖章

                            if ("2".Equals(sigantureEnabel) == true)  //写字板
                                row["WritImg"] = DBAccess.GetBigTextFromDB(trackTable, "MyPK", tk.MyPK, "WriteDB");

                            if ("4".Equals(sigantureEnabel) == true && "5".Equals(sigantureEnabel) == true)  //电子盖章  电子签名+盖章
                                row["WriteStamp"] = DBAccess.GetBigTextFromDB(trackTable, "MyPK", tk.MyPK, "FrmDB");

                            tkDt.Rows.Add(row);

                            #region //子流程的审核组件数据
                            if (tk.FID != 0
                                && tk.HisActionType == ActionType.StartChildenFlow && tkDt.Select("ParentNode=" + tk.NDFrom).Length == 0)
                            {
                                string[] paras = tk.Tag.Split('@');
                                string[] p1 = paras[1].Split('=');
                                string fk_flow = p1[1]; //子流程编号

                                string[] p2 = paras[2].Split('=');
                                string workId = p2[1]; //子流程ID.
                                int biaoji = 0;

                                WorkCheck subwc = new WorkCheck(fk_flow, int.Parse(fk_flow + "01"), Int64.Parse(workId), 0, wcDesc.GetValIntByKey("FWCTimeModel", 0));
                                string subtrackTable = "ND" + int.Parse(fk_flow) + "Track";
                                Tracks subtks = subwc.HisWorkChecks;
                                //取出来子流程的所有的节点。
                                Nodes subNds = new Nodes(fk_flow);
                                foreach (Node item in subNds)     //主要按顺序显示
                                {
                                    foreach (Track mysubtk in subtks)
                                    {
                                        if (item.NodeID != mysubtk.NDFrom)
                                            continue;

                                        /*输出该子流程的审核信息，应该考虑子流程的子流程信息, 就不考虑那样复杂了.*/
                                        if (mysubtk.HisActionType == ActionType.WorkCheck)
                                        {
                                            // 发起多个子流程时，发起人只显示一次
                                            if (mysubtk.NDFrom == int.Parse(fk_flow + "01") && biaoji == 1)
                                                continue;
                                            NodeWorkCheck subFrmCheck = new NodeWorkCheck("ND" + mysubtk.NDFrom);
                                            row = tkDt.NewRow();
                                            row["NodeID"] = mysubtk.NDFrom;
                                            row["NodeName"] = string.Format("(子流程){0}", mysubtk.NDFromT);
                                            row["Msg"] = mysubtk.MsgHtml;
                                            row["EmpFrom"] = mysubtk.EmpFrom;
                                            row["EmpFromT"] = mysubtk.EmpFromT;
                                            row["EmpTo"] = mysubtk.EmpTo;
                                            row["EmpToT"] = mysubtk.EmpToT;

                                            //获取部门
                                            DeptName = "";
                                            Arrays = mysubtk.NodeData.Split('@');
                                            foreach (string i in Arrays)
                                            {
                                                if (i.Contains("DeptName="))
                                                {
                                                    DeptName = i.Split('=')[1];
                                                }
                                            }
                                            row["DeptName"] = DeptName;
                                            row["RDT"] = mysubtk.RDT;
                                            row["ParentNode"] = tk.NDFrom;
                                            row["ActionType"] = mysubtk.HisActionType;
                                            row["Tag"] = mysubtk.Tag;
                                            if (subFrmCheck.SigantureEnabel != SigantureModel.WriteTablet2 && subFrmCheck.SigantureEnabel != SigantureModel.EleTablet3 && subFrmCheck.SigantureEnabel != SigantureModel.EleTabletAndSinge5)
                                                row["WritImg"] = "";
                                            else
                                                row["WritImg"] = DBAccess.GetBigTextFromDB(subtrackTable, "MyPK", mysubtk.MyPK, "WriteDB");
                                            if (subFrmCheck.SigantureEnabel != SigantureModel.EleSinge4 && subFrmCheck.SigantureEnabel != SigantureModel.EleTabletAndSinge5)
                                                row["WriteStamp"] = "";
                                            else
                                                row["WriteStamp"] = DBAccess.GetBigTextFromDB(subtrackTable, "MyPK", mysubtk.MyPK, "FrmDB");

                                            tkDt.Rows.Add(row);

                                            if (mysubtk.NDFrom == int.Parse(fk_flow + "01"))
                                            {
                                                biaoji = 1;
                                            }
                                        }
                                    }
                                }
                            }
                            #endregion
                            break;
                        default: break;
                    }
                }
                ds.Tables.Add(historyTracks);

                Flow fl = new Flow(gwf.FlowNo);
                if (fl.ItIsFullSA == true)
                {
                    WF_WorkOpt_OneWork oneWork = new WF_WorkOpt_OneWork();
                    string nodeIDs = oneWork.GetFutureNodeNotContainsEmps();
                    SelectAccpers sas = new SelectAccpers();
                    sas.Retrieve(SelectAccperAttr.WorkID, gwf.WorkID);

                    SelectAccpers csa = null;
                    foreach (string nodeID in nodeIDs.Split(","))
                    {
                        if (DataType.IsNullOrEmpty(nodeID) == true)
                            continue;
                        //是否已审核.
                        if (runNodes.Contains(nodeID + ",") == true)
                            continue;
                        //获取处理人
                        Entities ens = sas.GetEntitiesByKey(SelectAccperAttr.FK_Node, nodeID);
                        string empNos = "";
                        string empNames = "";
                        if (ens != null)
                        {
                            csa = (SelectAccpers)ens;
                            foreach (SelectAccper sa in csa.ToJavaList())
                            {
                                empNos += sa.EmpNo + ",";
                                empNames += sa.EmpName + ",";
                            }
                            empNos = empNos.Substring(0, empNos.Length - 1);
                            empNames = empNames.Substring(0, empNames.Length - 1);
                        }
                        row = tkDt.NewRow();
                        row["NodeID"] = nodeID;
                        Node mynd = (Node)nds.GetEntityByKey(nodeID);
                        row["NodeName"] = mynd.FWCNodeName;
                        row["ParentNode"] = 0;
                        row["RDT"] = "";
                        row["Msg"] = "未审批";
                        row["EmpFrom"] = empNos;
                        row["EmpFromT"] = empNames;
                        row["EmpTo"] = "";
                        row["EmpToT"] = "";
                        row["DeptName"] = "";
                        if (mynd.HisNodeType == NodeType.CCNode)
                            row["ActionType"] = (int)ActionType.CC;
                        if (wcDesc.GetValIntByKey("FWCTimeModel", 0) == 0)
                            tkDt.Rows.Add(0, row);
                        else
                            tkDt.Rows.Add(row);
                    }
                    ds.Tables.Add(tkDt);
                }

                //判断当前节点的是否可以审批
                if (wcDesc.HisFrmWorkCheckSta == FrmWorkCheckSta.Enable)
                {
                    Node nd = new Node(gwf.NodeID);
                    DataTable dtt = tkDt.Clone();
                    row = dtt.NewRow();
                    row["NodeID"] = gwf.NodeID;
                    row["NodeName"] = nd.FWCNodeName;
                    row["ParentNode"] = 0;
                    row["RDT"] = Dev2Interface.GetCheckRDT(gwf.FlowNo, gwf.WorkID, gwf.NodeID, WebUser.No);

                    row["Msg"] = BP.WF.Dev2Interface.GetCheckInfo(gwf.FlowNo, gwf.WorkID, gwf.NodeID, wcDesc.FWCDefInfo);
                    row["EmpFrom"] = WebUser.No;
                    row["EmpFromT"] = WebUser.Name;
                    row["EmpTo"] = WebUser.No;
                    row["EmpToT"] = WebUser.Name;
                    row["DeptName"] = WebUser.DeptName;
                    row["ActionType"] = ActionType.Forward;
                    row["Tag"] = Dev2Interface.GetCheckTag(gwf.FlowNo, gwf.WorkID, gwf.NodeID, WebUser.No);


                    dtt.Rows.Add(row);
                    dtt.TableName = "MyTrack";
                    ds.Tables.Add(dtt);
                }

                string info = BP.Tools.Json.ToJson(ds);
                return Return_Info(200, "获取成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "根据WorkID获得流程轨迹失败", ex.Message);
            }
        }


        /// <summary>
        /// 获得流程下节点的标记.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="flowNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_NodesMark(string token, string flowNo)
        {
            //根据token登录
            Port_GenerToken(token);
            DataSet ds = new DataSet();
            try
            {
                //执行转换.
                flowNo = Dev2Interface.Flow_TurnFlowMark2FlowNo(flowNo);

                //获得节点的Mark.
                string sql = "SELECT NodeID,Name,NodeMark FROM WF_Node WHERE FK_Flow='" + flowNo + "'";
                DataTable dt = DBAccess.RunSQLReturnTable(sql);
                string info = BP.Tools.Json.ToJson(dt);
                return Return_Info(200, "获取成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "获得流程下节点的标记失败", ex.Message);
            }
        }
        /// <summary>
        /// 获得1个节点的标记.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="nodeID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_NodeMark(string token, int nodeID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                return Return_Info(200, "获取成功", Flow_NodeMarkExt(nodeID));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "获得1个节点的标记失败", ex.Message);
            }
        }
        protected string Flow_NodeMarkExt(int nodeID)
        {
            //获得节点的Mark.
            string sql = "SELECT NodeMark FROM WF_Node WHERE NodeID='" + nodeID + "'";
            return DBAccess.RunSQLReturnStringIsNull(sql, nodeID.ToString());
        }
        protected DataTable Flow_NodesMarkExt(string flowNo)
        {
            //获得节点的Mark.
            string sql = "SELECT NodeID,Name FROM WF_Node WHERE FK_Flow='" + flowNo + "'";
            return DBAccess.RunSQLReturnTable(sql);
        }
        protected string Flow_NodeMarkExt(int nodeID, DataTable dt)
        {
            foreach (DataRow dr in dt.Rows)
            {
                if (int.Parse(dr[0].ToString()) == nodeID)
                {
                    string str = dr[2].ToString();
                    if (DataType.IsNullOrEmpty(str) == true)
                        return nodeID.ToString();
                    return str;
                }
            }
            throw new Exception("err@没有找到NodeID=" + nodeID + "的mark");
        }
        /// <summary>
        /// 是否可以处理当前的工作
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_IsCanDealWork(string token, long workID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                GenerWorkFlow gwf = new GenerWorkFlow(workID);
                string todoEmps = gwf.TodoEmps;
                bool isCanDo = false;
                if (gwf.NodeID.ToString().EndsWith("01") == true)
                {
                    if (gwf.Starter.Equals(WebUser.No) == false)
                        isCanDo = false; //处理开始节点发送后，撤销的情况，第2个节点打开了，第1个节点撤销了,造成第2个节点也可以发送下去.
                    else
                        isCanDo = true; // 开始节点不判断权限.
                }
                else
                {
                    isCanDo = todoEmps.Contains(";" + WebUser.No + ",");
                    if (isCanDo == false)
                        isCanDo = Dev2Interface.Flow_IsCanDoCurrentWork(workID, WebUser.No);
                }
                return Return_Info(200, "获取是否可以处理当前的工作成功", isCanDo == true ? "1" : "0");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "获取是否可以处理当前的工作失败", ex.Message);
            }
        }
        /// <summary>
        /// 设置草稿
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID">流程实例ID</param>
        /// <param name="flowNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_SetDraft(string token, long workID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                Dev2Interface.Node_SetDraft(workID);
                return Return_Info(200, "设置草稿成功", "设置草稿成功");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "设置草稿失败", ex.Message);
            }
        }
        /// <summary>
        /// 删除指定的会签人
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workid">流程实例WorkID</param>
        /// <param name="empNo">人员编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_HuiQian_Delete(string token, long workid, string empNo)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                Dev2Interface.Node_HuiQian_Delete(workid, empNo);
                return Return_Info(200, "删除会签人员成功", "1");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "删除会签人员失败", ex.Message);
            }
        }
        /// <summary>
        /// 会签：增加会签人
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workid"></param>
        /// <param name="empNos">人员编号:001,002</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_HuiQian_AddEmps(string token, long workid, string empNos)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                Dev2Interface.Node_HuiQian_AddEmps(workid, "0", empNos);
                return Return_Info(200, "增加会签人员成功", "1");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "增加会签人员失败", ex.Message);
            }
        }
        /// <summary>
        /// 会签：执行发送.
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workid"></param>
        /// <param name="toNodeID">到达的节点ID, 也可以是节点Mark.</param>
        /// <returns>执行的结果</returns>
        [HttpGet, HttpPost]
        public object Node_HuiQianDone(string token, long workid, string toNodeIDStr = "0")
        {
            //根据token登录
            Port_GenerToken(token);

            GenerWorkFlow gwf = new GenerWorkFlow(workid);
            int toNodeID = DealNodeIDStr(toNodeIDStr, gwf.FlowNo);

            try
            {
                Dev2Interface.Node_HuiQianDone(workid, toNodeID);
                return Return_Info(200, "会签执行发送成功", "执行成功");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "会签执行发送失败", ex.Message);
            }
        }
        /// <summary>
        /// 把当前工作移交给指定的人员
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID"></param>
        /// <param name="toEmpNo"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_Shift(string token, long workID, string toEmpNo, string msg)
        {

            try
            {
                //根据token登录
                Port_GenerToken(token);

                msg = Dev2Interface.Node_Shift(workID, toEmpNo, msg);
                return Return_Info(200, "把当前工作移交给指定的人员成功", msg);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "把当前工作移交给指定的人员失败", ex.Message);
            }
        }
        /// <summary>
        /// 给当前的工作增加处理人
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID"></param>
        /// <param name="empNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_AddTodolist(string token, long workID, string empNo)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                Dev2Interface.Node_AddTodolist(workID, empNo);
                return Return_Info(200, "给当前的工作增加处理人成功", "增加人员成功");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "给当前的工作增加处理人失败", ex.Message);
            }
        }
        /// <summary>
        /// 根据ID获取当前的流程实例信息
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_GenerWorkFlow(string token, long workID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                GenerWorkFlow gwf = new GenerWorkFlow(workID);
                return Return_Info(200, "根据ID获取当前的流程实例信息成功", gwf.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "根据ID获取当前的流程实例信息失败", ex.Message);
            }
        }
        /// <summary>
        /// 设置参数集合:保存参数到 WF_GenerWorkFlow,用于方向条件的判断,接受人规则.
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID">工作ID</param>
        /// <param name="paras">要保存的参数，用于控制流程转向等的参数变量，注意:数据并没有保存到表单, 格式： @Key1=Val2@Key2=Val2 </param>
        /// <returns>执行结果</returns>
        [HttpGet, HttpPost]
        public object Flow_SaveParas(string token, long workID, string paras)
        {
            //根据token登录
            try
            {
                Port_GenerToken(token);
                Dev2Interface.Flow_SaveParas(workID, paras);
                return Return_Info(200, "参数保存成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "参数保存失败", ex.Message);
            }
        }

        /// <summary>
        /// 设置参数:保存参数到 WF_GenerWorkFlow,用于方向条件的判断,接受人规则.
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID">工作ID</param>
        /// <param name="key">参数key</param>
        /// <param name="val">参数值</param>
        /// <returns>返回执行结果</returns>
        [HttpGet, HttpPost]
        public object Flow_SavePara(string token, long workID, string key, string val)
        {
            //根据token登录 
            try
            {
                Port_GenerToken(token);
                GenerWorkFlow gwf = new GenerWorkFlow(workID);
                gwf.SetPara(key, val);
                gwf.Update();
                return Return_Info(200, "参数保存成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "参数保存失败", ex.Message);
            }
        }
        /// <summary>
        /// 保存节点表单数据
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workID">工作id</param>
        /// <param name="keyVals">要保存的主表数据，格式： @Key1=Val2@Key2=Val2  @Em=zhangsan@ccflow.org@Tel=18660153393 </param>
        /// <param name="dtlJSON">从表数据,格式： 多个从表的表名按照与从表控件的ID对应，字段与控件的字段对应。 </param>
        /// <param name="athJSON">附件数据,格式： 多个附件: 格式 FileName, FileUrl。一定是 FileName 在前，FileUrl 在后。
        /// 格式： {'Ath1':[{'FileName':'我的附件1.doc','FileUrl':'http://localst:9003:/xxx.docx'},{'FileName':'我的附件2.doc','FileUrl':'http://localst:9003:/xxx.docx'}]} </param>
        /// <returns>执行结果</returns>
        [HttpGet, HttpPost]
        public object Node_SaveWork(string token, long workID, string keyVals, string dtlJSON = null, string athJSON = null)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                AtPara ap = new AtPara(keyVals);

                DataSet dsDtls = null;
                if (dtlJSON != null)
                    dsDtls = BP.Tools.Json.ToDataSet(dtlJSON);

                DataSet dsAths = null;
                if (athJSON != null)
                    dsAths = BP.Tools.Json.ToDataSet(athJSON);

                //执行保存方法.
                BP.WF.Dev2Interface.Node_SaveWork(workID, ap.HisHT, dsDtls, dsAths);

                return Return_Info(200, "表单主表数据,执行成功.", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "工作保存失败", ex.Message);
            }
        }
        /// <summary>
        /// 设置标题
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_SetTitle(string token, long workID, string title)
        {
            //根据token登录
            Port_GenerToken(token);

            try
            {
                Dev2Interface.Flow_SetFlowTitle(null, workID, title);
                return Return_Info(200, "标题设置成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "标题设置失败", ex.Message);
            }
        }

        /// <summary>
        /// 待办、在途、消息..数量接口
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_TodoNums(string token)
        {
            //根据token登录
            Port_GenerToken(token);

            try
            {
                string json = BP.CCBill.WF_CCBill_Portal.Default_TodoNums();
                return Return_Info(200, "获取成功", json);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "获取失败", ex.Message);
            }
        }
        /// <summary>
        /// 发送接口
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID">工作实例WorkID</param>
        /// <param name="toNodeIDStr">到达的下一个节点,默认为0,可以是节点Mark. </param>
        /// <param name="toEmps">下一个节点的接收人，多个人用逗号分开比如:zhangsan,lisi</param>
        /// <param name="paras">参数，保存到WF_GenerWorkFlow,用与参数条件,格式: @key1=val1@Key2=Val2</param>
        /// <param name="checkNote">审核意见:启用了审核组件，就需要填写审核意见,负责不让发送。</param>
        /// <returns>执行结果,可以直接提示给用户.</returns>
        [HttpGet, HttpPost]
        public object Node_SendWork_ReJSON(string token, long workID, string toNodeIDStr = "0", string toEmps = "", string paras = "", string checkNote = "")
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {

                //保存参数.
                if (DataType.IsNullOrEmpty(paras) == false)
                    Dev2Interface.Flow_SaveParas(workID, paras);

                //写入审核意见.
                if (DataType.IsNullOrEmpty(checkNote) == false)
                    Dev2Interface.Node_WriteWorkCheck(workID, checkNote, null, null, null);

                //获得流程编号.
                string flowNo = DBAccess.RunSQLReturnString("SELECT FK_Flow FROM WF_GenerWorkFlow WHERE WorkID=" + workID);
                int toNodeID = DealNodeIDStr(toNodeIDStr, flowNo); //获得流程标识.

                //执行发送.
                SendReturnObjs objs = Dev2Interface.Node_SendWork(flowNo, workID, null, null, toNodeID, toEmps);
                string msg = objs.ToJson();
                return Return_Info(200, "发送成功", msg);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "发送失败", ex.Message);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workID"></param>
        /// <param name="toNodeIDStr"></param>
        /// <param name="toEmps"></param>
        /// <param name="paras"></param>
        /// <param name="checkNote"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_SendWork(string token, long workID, string toNodeIDStr = "0", string toEmps = "", string paras = "", string checkNote = "")
        {
            try
            {
                //根据token登录
                Port_GenerToken(token);

                //保存参数.
                if (DataType.IsNullOrEmpty(paras) == false)
                    Dev2Interface.Flow_SaveParas(workID, paras);

                //写入审核意见.
                if (DataType.IsNullOrEmpty(checkNote) == false)
                    Dev2Interface.Node_WriteWorkCheck(workID, checkNote, null, null, null);

                string flowNo = DBAccess.RunSQLReturnString("SELECT FK_Flow FROM WF_GenerWorkFlow WHERE WorkID=" + workID);
                int toNodeID = DealNodeIDStr(toNodeIDStr, flowNo);

                //执行发送.
                SendReturnObjs objs = Dev2Interface.Node_SendWork(flowNo, workID, null, null, toNodeID, toEmps);

                string msg = objs.ToMsgOfText();

                //保存参数到Rpt表.
                if (!DataType.IsNullOrEmpty(paras))
                {
                    Flow flow = new Flow();
                    flow.No = flowNo;
                    if (flow.RetrieveFromDBSources() > 0)
                    {
                        string sql = "UPDATE " + flow.PTable + " SET AtPara = '" + paras + "' WHERE OID = " + workID;
                        DBAccess.RunSQL(sql);
                    }
                }
                return Return_Info(200, "发送成功", msg);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "发送失败", ex.Message);
            }

        }

        /// <summary>
        /// 根据流程编号获取流程实例
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="flowNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DB_GenerWorkFlow(string token, string flowNo)
        {
            //根据token登录
            Port_GenerToken(token);

            try
            {
                GenerWorkFlows gwfs = new GenerWorkFlows();
                QueryObject qo = new QueryObject(gwfs);
                qo.AddWhere("FK_Flow", flowNo);
                qo.addAnd();
                qo.AddWhere("WFState", ">", 1);
                qo.addOrderBy("RDT");
                qo.DoQuery();
                return Return_Info(200, "根据流程编号获取流程实例成功", gwfs.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(200, "根据流程编号获取流程实例失败", ex.Message);
            }
        }

        /// <summary>
        /// 获取当前节点可以退回到的节点集合
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="nodeID">节点ID</param>
        /// <param name="workID">工作实例WorkID</param>
        /// <param name="fid">父WorkID</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DB_GenerWillReturnNodes(string token, int nodeID, long workID)
        {
            try
            {
                //根据token登录
                Port_GenerToken(token);
                DataTable dt = Dev2Interface.DB_GenerWillReturnNodes(workID);
                return Return_Info(200, "获取当前节点可以退回到的节点集合成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "获取当前节点可以退回到的节点集合失败", ex.Message);
            }
        }
        /// <summary>
        /// 根据节点标记获取节点ID
        /// </summary>
        /// <param name="nodeIDStr">节点标记</param>
        /// <param name="flowNo">流程编号</param>
        /// <returns></returns>
        protected int DealNodeIDStr(string nodeIDStr, string flowNo)
        {
            int returnToNodeID = 0;
            if (DataType.IsNullOrEmpty(nodeIDStr) == true)
                return 0;
            if (DataType.IsNumStr(nodeIDStr) == true)
                returnToNodeID = int.Parse(nodeIDStr);
            else
                returnToNodeID = DBAccess.RunSQLReturnValInt("SELECT NodeID FROM WF_Node WHERE FK_Flow='" + flowNo + "' AND Mark='" + nodeIDStr + "'");
            return returnToNodeID;
        }
        /// <summary>
        /// 当前节点执行退回操作
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID">工作实例WorkID</param>
        /// <param name="returnToNodeIDStr">退回到的节点,可以是节点的Mark</param>
        /// <param name="returnToEmp">退回到的接收人</param>
        /// <param name="msg">退回原因</param>
        /// <param name="isBackToThisNode">是否原路返回到当前节点</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_ReturnWork(string token, long workID, string returnToNodeIDStr, string returnToEmp, string msg, bool isBackToThisNode)
        {

            try
            {
                //根据token登录
                Port_GenerToken(token);

                GenerWorkFlow gwf = new GenerWorkFlow(workID);
                //获取真实的NodeID.
                int returnToNodeID = DealNodeIDStr(returnToNodeIDStr, gwf.FlowNo);

                if (returnToNodeID == 0)
                {
                    DataTable dt = Dev2Interface.DB_GenerWillReturnNodes(workID);
                    if (dt.Rows.Count == 1)
                    {
                        returnToNodeID = int.Parse(dt.Rows[0]["No"].ToString());
                        returnToEmp = dt.Rows[0]["Rec"].ToString();
                    }
                    if (dt.Rows.Count > 1)
                    {
                        //获取第一个节点
                        returnToNodeID = int.Parse(dt.Rows[0]["No"].ToString());
                        if (returnToNodeID == Int32.Parse(Int32.Parse(gwf.FlowNo) + "01"))
                            returnToEmp = dt.Rows[0]["Rec"].ToString();
                        else
                        {
                            string toNodes = "";
                            foreach (DataRow dr in dt.Rows)
                            {
                                toNodes += "[" + dr[0] + "-" + dr[1] + "-" + dr[3] + "]、";
                            }
                            return Return_Info(500, "当前节点执行退回操作失败", "可以退回到的节点有多个" + toNodes + ", 请输入退回到的节点");
                        }

                    }
                }


                //执行退回.
                string strs = Dev2Interface.Node_ReturnWork(workID,
                    returnToNodeID, returnToEmp, msg, isBackToThisNode);
                return Return_Info(200, "当前节点执行退回操作成功", strs);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "当前节点执行退回操作失败", ex.Message);
            }
        }
        /// <summary>
        /// 催办
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workIDs">工作实例的WorkID集合</param>
        /// <param name="msg">催办信息</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_DoPress(string token, string workIDs, string msg)
        {
            //根据token登录
            Port_GenerToken(token);
            if (DataType.IsNullOrEmpty(workIDs))
            {
                return Return_Info(500, "执行批量催办的WorkIDs不能为空", "");
            }
            string[] strs = workIDs.Split(',');

            if (msg == null)
                msg = "需要您处理待办工作.";
            try
            {
                string info = "";
                foreach (string workidStr in strs)
                {
                    if (DataType.IsNullOrEmpty(workidStr) == true)
                        continue;

                    info += "@" + Dev2Interface.Flow_DoPress(int.Parse(workidStr), msg, true);
                }
                return Return_Info(200, "执行批量催办成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行批量催办失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量审核
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workIDs"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object CC_BatchCheckOver(string token, string workIDs)
        {
            //根据token登录
            Port_GenerToken(token);

            if (DataType.IsNullOrEmpty(workIDs))
            {
                return Return_Info(500, "执行批量审批的WorkIDs不能为空", "");
            }
            try
            {
                string str = Dev2Interface.Node_CC_SetCheckOverBatch(workIDs);

                return Return_Info(200, "批量审核成功", str);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "批量审核失败", ex.Message);
            }
        }
        #endregion 节点方法.

        #region 流程方法.
        /// <summary>
        /// 重新设置流程标题
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workID">重新设置流程标题</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_ReSetFlowTitle(string token, long workID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                Dev2Interface.Flow_ReSetFlowTitle(workID);
                return Return_Info(200, "设置成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "删除失败", ex.Message);
            }
        }
        /// <summary>
        /// 近期工作
        /// </summary>
        /// <param name="token">密钥</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_RecentWorkInit(string token)
        {
            //根据token登录
            Port_GenerToken(token);

            try
            {
                string st1r = Dev2Interface.Flow_RecentWorkInit();
                return Return_Info(200, "查询成功", st1r);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "查询失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量删除流程
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workIDs"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_BatchDeleteByFlag(string token, string workIDs)
        {
            //根据token登录
            Port_GenerToken(token);

            if (DataType.IsNullOrEmpty(workIDs))
            {
                return Return_Info(500, "批量删除的WorkIDs不能为空", "");
            }
            try
            {
                string[] strs = workIDs.Split(',');
                foreach (string workidStr in strs)
                {
                    if (DataType.IsNullOrEmpty(workidStr) == true)
                        continue;

                    string st1r = Dev2Interface.Flow_DoDeleteFlowByFlag(long.Parse(workidStr), "删除", true);
                }
                return Return_Info(200, "删除成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "删除失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量删除
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workIDs">工作实例WorkIDs</param>
        /// <param name="isDeleteSubFlows">是否删除子流程</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_BatchDeleteByReal(string token, string workIDs, bool isDeleteSubFlows)
        {
            if (DataType.IsNullOrEmpty(workIDs))
                return Return_Info(500, "批量删除的WorkIDs不能为空", "");

            try
            {
                //根据token登录
                Port_GenerToken(token);

                string[] strs = workIDs.Split(',');

                foreach (string workidStr in strs)
                {
                    if (DataType.IsNullOrEmpty(workidStr) == true)
                        continue;

                    string st1r = Dev2Interface.Flow_DoDeleteFlowByReal(long.Parse(workidStr), isDeleteSubFlows);
                }
                return Return_Info(200, "删除成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "删除失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量恢复逻辑删除的流程
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workIDs"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_BatchDeleteByFlagAndUnDone(string token, string workIDs)
        {
            if (DataType.IsNullOrEmpty(workIDs))
                return Return_Info(500, "批量撤销删除的WorkIDs不能为空", "");

            try
            {
                //根据token登录
                Port_GenerToken(token);


                string[] strs = workIDs.Split(',');

                foreach (string workidStr in strs)
                {
                    if (DataType.IsNullOrEmpty(workidStr) == true)
                        continue;

                    string st1r = Dev2Interface.Flow_DoUnDeleteFlowByFlag(null, int.Parse(workidStr), "删除");
                }
                return Return_Info(200, "撤销删除成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "撤销删除失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量撤回
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workids"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_DoUnSend(string token, string workIDs)
        {

            //获取可以发起的列表
            string[] strs = workIDs.Split(',');
            if (DataType.IsNullOrEmpty(workIDs))
                return Return_Info(500, "批量撤回的WorkIDs不能为空", "");

            try
            {
                //根据token登录
                Port_GenerToken(token);


                string info = "";
                foreach (string workidStr in strs)
                {
                    if (DataType.IsNullOrEmpty(workidStr) == true)
                        continue;

                    info += Dev2Interface.Flow_DoUnSend(null, long.Parse(workidStr), 0, 0);
                }
                return Return_Info(200, "批量撤回成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "批量撤回失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量删除草稿
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workids"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_DeleteDraft(string token, string workIDs)
        {

            string[] strs = workIDs.Split(',');
            if (DataType.IsNullOrEmpty(workIDs))
                return Return_Info(500, "批量删除草稿的WorkIDs不能为空", "");
            try
            {
                //根据token登录
                Port_GenerToken(token);


                foreach (string workidStr in strs)
                {
                    if (DataType.IsNullOrEmpty(workidStr) == true)
                        continue;

                    Dev2Interface.Node_DeleteDraft(long.Parse(workidStr));
                }
                return Return_Info(200, "删除成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "删除失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量结束流程
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="workids"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_DoFlowOver(string token, string workIDs)
        {
            string[] strs = workIDs.Split(',');
            if (DataType.IsNullOrEmpty(workIDs))
                return Return_Info(500, "批量结束流程的WorkIDs不能为空", "");
            try
            {
                //根据token登录
                Port_GenerToken(token);

                foreach (string workidStr in strs)
                {
                    if (DataType.IsNullOrEmpty(workidStr) == true)
                        continue;

                    Dev2Interface.Flow_DoFlowOver(long.Parse(workidStr), "批量结束", WFState.CompleteEnd);
                }
                return Return_Info(200, "执行成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }

        }
        /// <summary>
        /// 设置父流程信息
        /// </summary>
        /// <param name="subFlowNo">子流程编号</param>
        /// <param name="subFlowWorkID">子流程workid</param>
        /// <param name="parentWorkID">父流程WorkID</param>
        /// <param name="isCopyData">是否要copy父流程的数据</param
        [HttpGet, HttpPost]
        public object SetParentInfo(string token, string subFlowNo, Int64 subFlowWorkID, Int64 parentWorkID, bool isCopyData = false)
        {
            try
            {
                //根据token登录
                Port_GenerToken(token);

                Dev2Interface.SetParentInfo(subFlowNo, subFlowWorkID, parentWorkID, isCopyData);

                return Return_Info(200, "执行成功", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        #endregion 节点方法.

        #region 批处理相关
        /// <summary>
        /// 批量处理
        /// </summary>
        /// <param name="token">密钥</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Batch_Init(string token)
        {
            //根据token登录
            Port_GenerToken(token);

            try
            {
                var handle = new BP.WF.HttpHandler.WF();
                string str = handle.Batch_Init();
                return Return_Info(200, "执行成功", str);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }

        }
        /// <summary>
        /// 根据NodeID获取节点信息
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="nodeID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object En_Node(string token, int nodeID)
        {

            try
            {
                //根据token登录
                Port_GenerToken(token);
                Node node = new Node(nodeID);

                return Return_Info(200, "根据NodeID获取节点信息成功", node.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }

        }
        /// <summary>
        /// 根据流程编号获取流程信息
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="flowNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object En_Flow(string token, string flowNo)
        {
            try
            {
                //根据token登录
                Port_GenerToken(token);

                Flow flow = new Flow(flowNo);
                return Return_Info(200, "根据流程编号获取流程信息成功", flow.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "根据流程编号获取流程信息失败", ex.Message);
            }

        }
        /// <summary>
        /// 根据流程编号获取流程信息
        /// </summary>
        /// <param name="token">密钥</param>
        /// <param name="nodeID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object WorkCheckModel_Init(string token, int nodeID)
        {
            try
            {
                //根据token登录
                Port_GenerToken(token);

                DataSet ds = new DataSet();
                //获取节点信息
                Node nd = new Node(nodeID);
                Flow fl = nd.HisFlow;
                ds.Tables.Add(nd.ToDataTableField("WF_Node"));
                string sql = "";
                if (nd.ItIsSubThread == true)
                {
                    sql = "SELECT a.*, b.Starter,b.StarterName,b.ADT,b.WorkID FROM " + fl.PTable
                              + " a , WF_EmpWorks b WHERE a.OID=B.FID AND b.WFState Not IN (7) AND b.FK_Node=" + nd.NodeID
                              + " AND b.FK_Emp='" + WebUser.No + "'";
                }
                else
                {
                    sql = "SELECT a.*, b.Starter,b.StarterName,b.ADT,b.WorkID FROM " + fl.PTable
                            + " a , WF_EmpWorks b WHERE a.OID=B.WorkID AND b.WFState Not IN (7) AND b.FK_Node=" + nd.NodeID
                            + " AND b.FK_Emp='" + WebUser.No + "'";
                }

                //获取待审批的流程信息集合
                DataTable dt = DBAccess.RunSQLReturnTable(sql);
                dt.TableName = "Works";
                ds.Tables.Add(dt);
                //获取按钮权限
                BtnLab btnLab = new BtnLab(nodeID);
                ds.Tables.Add(btnLab.ToDataTableField("Sys_BtnLab"));
                //获取字段属性
                MapAttrs attrs = new MapAttrs("ND" + nodeID);
                //获取实际中需要展示的列.
                string batchParas = nd.GetParaString("BatchFields");
                MapAttrs realAttr = new MapAttrs();
                if (DataType.IsNullOrEmpty(batchParas) == false)
                {
                    string[] strs = batchParas.Split(',');
                    foreach (string str in strs)
                    {
                        if (string.IsNullOrEmpty(str)
                            || str.Contains("@PFlowNo") == true)
                            continue;

                        foreach (MapAttr attr in attrs)
                        {
                            if (str != attr.KeyOfEn)
                                continue;

                            realAttr.AddEntity(attr);
                        }
                    }
                }
                ds.Tables.Add(realAttr.ToDataTableField("Sys_MapAttr"));
                return Return_Info(200, "根据节点编号获取流程信息成功", BP.Tools.Json.ToJson(ds));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "根据节点编号获取流程信息失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量处理获取当前到达下一个节点的集合信息
        /// </summary>
        /// <param name="token"></param>
        /// <param name="nodeID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Batch_InitDDL(string token, int nodeID)
        {

            try
            {
                //根据token登录
                Port_GenerToken(token);

                GenerWorkFlow gwf = new GenerWorkFlow();
                Node nd = new Node(nodeID);
                gwf.TodoEmps = WebUser.No + ",";
                DataTable mydt = Dev2Interface.Node_GenerDTOfToNodes(gwf, nd);
                return Return_Info(200, "执行成功", BP.Tools.Json.ToJson(mydt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量发送
        /// </summary>
        /// <param name="token"></param>
        /// <param name="flowNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object WorkCheckModel_Send(string token, string flowNo)
        {

            try
            {
                //根据token登录
                Port_GenerToken(token);
                var handle = new BP.WF.HttpHandler.WF_WorkOpt_Batch();
                string str = handle.WorkCheckModel_Send();
                return Return_Info(200, "执行成功", str);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }

        }
        /// <summary>
        /// 批量删除
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workIDs"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Batch_Delete(string token, string workIDs)
        {


            try
            {
                //根据token登录
                Port_GenerToken(token);

                if (DataType.IsNullOrEmpty(workIDs) == true)
                    return Return_Info(500, "批量删除成功", "没有选择需要处理的工作");
                string msg = "";
                GenerWorkFlows gwfs = new GenerWorkFlows();
                gwfs.RetrieveIn("WorkID", workIDs);
                foreach (GenerWorkFlow gwf in gwfs)
                {
                    msg += "@对工作(" + gwf.Title + ")处理情况如下。<br>";
                    string mes = Dev2Interface.Flow_DoDeleteFlowByFlag(gwf.WorkID, "批量删除", true);
                    msg += mes;
                    msg += "<hr>";
                }
                return Return_Info(200, "批量删除成功", msg);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }

        }
        #endregion 批处理相关

        #region 其他方法.
        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="dtFrom">日期从</param>
        /// <param name="dtTo">日期到</param>
        /// <param name="scop">范围</param>
        /// <param name="pageIdx">分页</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Search_Init(string token, string key, string dtFrom, string dtTo, string scop, int pageIdx)
        {
            try
            {
                //根据token登录
                Port_GenerToken(token);

                GenerWorkFlows gwfs = new GenerWorkFlows();
                //创建查询对象.
                QueryObject qo = new QueryObject(gwfs);
                if (DataType.IsNullOrEmpty(key) == false)
                {
                    qo.AddWhere(GenerWorkFlowAttr.Title, " LIKE ", "'%" + key + "%'");
                    qo.addAnd();
                }

                //我参与的.
                if (scop.Equals("0") == true)
                    qo.AddWhere(GenerWorkFlowAttr.Emps, "LIKE", "'%@" + WebUser.No + "%'");

                //我发起的.
                if (scop.Equals("1") == true)
                    qo.AddWhere(GenerWorkFlowAttr.Starter, "=", WebUser.No);

                //我部门发起的.
                if (scop.Equals("2") == true)
                    qo.AddWhere(GenerWorkFlowAttr.FK_Dept, "=", WebUser.DeptNo);


                //任何一个为空.
                if (DataType.IsNullOrEmpty(dtFrom) == true || DataType.IsNullOrEmpty(dtTo) == true)
                {

                }
                else
                {
                    qo.addAnd();
                    qo.AddWhere(GenerWorkFlowAttr.RDT, ">=", dtFrom, "dtForm");
                    qo.addAnd();
                    qo.AddWhere(GenerWorkFlowAttr.RDT, "<=", dtTo, "dtTo");
                }

                var count = qo.GetCount(); //获得总数.

                qo.DoQuery("WorkID", 20, pageIdx);
                //   qo.DoQuery(); // "WorkID", 20, pageIdx);


                DataTable dt = gwfs.ToDataTableField("gwls");

                //创建容器.
                DataSet ds = new DataSet();
                ds.Tables.Add(dt); //增加查询对象.

                //增加数量.
                DataTable mydt = new DataTable();
                mydt.TableName = "count";
                mydt.Columns.Add("CC");
                DataRow dr = mydt.NewRow();
                dr[0] = count.ToString(); //把数量加进去.
                mydt.Rows.Add(dr);
                ds.Tables.Add(mydt);
                return Return_Info(200, "查询成功", BP.Tools.Json.ToJson(ds));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "查询失败", ex.Message);
            }
        }
        /// <summary>
        /// 根据Token值登录
        /// </summary>
        /// <param name="token"></param>
        protected void Port_GenerToken(string token)
        {
            Dev2Interface.Port_LoginByToken(token);
        }

        protected HttpResponseMessage ReturnMessage(string message)
        {
            return new HttpResponseMessage { Content = new StringContent(message, System.Text.Encoding.GetEncoding("UTF-8"), "application/json") };
        }
        #endregion 其他方法.
        /// <summary>
        /// 
        /// </summary>
        /// <param name="token"></param>
        /// <param name="keyword"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public string Search_Emps(string token, string keyword)
        {
            //根据token登录
            Port_GenerToken(token);

            string sqlTemplate = "";
            string execSql = "";
            if (DataType.IsNullOrEmpty(keyword))
            {
                return "err@请输入关键字";
            }
            if (SystemConfig.CCBPMRunModel == CCBPMRunModel.Single)
            {
                sqlTemplate = "SELECT A.No,A.Name,B.Name AS DeptName FROM Port_Emp A,Port_Dept B WHERE A.FK_Dept=B.No AND ( A.No LIKE '%@Key%' OR A.Name LIKE '%@Key%' OR PinYin LIKE  '%@Key%') AND A.EmpSta = 0 Order By A.Idx ";
                execSql = sqlTemplate.Replace("@Key", keyword);
                Console.WriteLine(execSql);
            }
            else
            {
                sqlTemplate = "SELECT A.No,A.Name,B.Name AS DeptName FROM Port_Emp A,Port_Dept B WHERE A.FK_Dept=B.No AND ( A.No LIKE '%@Key%' OR A.Name LIKE '%@Key%' OR PinYin LIKE  '%@Key%' AND A.OrgNo='@WebUser.OrgNo') AND A.EmpSta = 0 Order By A.Idx ";
                execSql = sqlTemplate.Replace("@Key", keyword);
                execSql = execSql.Replace("@WebUser.OrgNo", WebUser.OrgNo);
            }
            if (SystemConfig.AppCenterDBType == DBType.MSSQL)
                execSql = execSql.Replace("SELECT", "SELECT Top 20");
            else if (SystemConfig.AppCenterDBType == DBType.Oracle
                    || SystemConfig.AppCenterDBType == DBType.DM
                    || SystemConfig.AppCenterDBType == DBType.KingBaseR3
                    || SystemConfig.AppCenterDBType == DBType.KingBaseR6
                    || SystemConfig.AppCenterDBType == DBType.GBASE8CByOracle)
                execSql = "SELECT  * FROM(" + execSql + ")  WHERE ROWNUM =1";
            else
                execSql += " LIMIT 20";

            DataTable dt = DBAccess.RunSQLReturnTable(execSql);

            if (SystemConfig.AppCenterDBFieldCaseModel == FieldCaseModel.UpperCase)
            {
                dt.Columns["NO"].ColumnName = "No";
                dt.Columns["NAME"].ColumnName = "Name";
                dt.Columns["DEPTNAME"].ColumnName = "DeptName";
            }

            if (SystemConfig.AppCenterDBFieldCaseModel == FieldCaseModel.Lowercase)
            {
                dt.Columns["no"].ColumnName = "No";
                dt.Columns["name"].ColumnName = "Name";
                dt.Columns["deptname"].ColumnName = "DeptName";
            }

            return BP.Tools.Json.ToJson(dt);
        }

        #region vsto 组件功能.
        /// <summary>
        /// 获取信息
        /// </summary>
        /// <param name="token">登陆人员的信息</param>
        /// <param name="workID">工作ID</param>
        /// <returns>执行结果</returns>
        [HttpGet, HttpPost]
        public object VSTOWord_GongWen_Init(string token, long workID)
        {
            //根据token登录.
            Port_GenerToken(token);
            try
            {
                GenerWorkFlow gwf = new GenerWorkFlow(workID);

                return Return_Info(200, "执行成功", gwf.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        /// <summary>
        /// 获得文件
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object VSTOWord_GongWen_GetFile(string token, long workID)
        {
            return Return_Info(200, "执行成功", "xxxx");
        }
        #endregion vsto 组件功能.

        #region 单据方法.Bill
        /// <summary>
        /// 创建单据ID
        /// </summary>
        /// <param name="token">token</param>
        /// <param name="frmID">单据ID</param>
        /// <returns>int类型的 64位的不重复的数值，并且与流程WorkID不重复，用它可以做主键，手工的或者使用接口的模式向单据初始化数据.</returns>
        [HttpGet, HttpPost]
        public object Bill_CreateBlankBillID(string token, string frmID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                Int64 workID = BP.CCBill.Dev2Interface.CreateBlankBillID(frmID);
                return Return_Info(200, "执行成功", workID.ToString());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 设置草稿: 把单据设置为草稿,在创建单据实例的时候,就是一个新的workid了
        /// </summary>
        /// <param name="token">token</param>
        /// <param name="workID">实例ID</param>
        /// <returns>执行结果</returns>
        [HttpGet, HttpPost]
        public object Bill_SaveAsDraft(string token, Int64 workID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                string info = BP.CCBill.Dev2Interface.SaveAsDraft(workID);
                return Return_Info(200, "执行成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 保存数据
        /// </summary>
        /// <param name="token">token</param>
        /// <param name="workID">单据实例ID</param>
        /// <param name="bodyData">主表数据(可为空)格式:@Key1=Val1@Key2=Val2 比如: @Tel=18660153393@Addr=山东济南高新区碧桂园</param>
        /// <param name="dtlsJSON">从表数据(可为空)格式Demo: "{'ND601CheLiangXinXi':[{'ChePaiHao':'黑A888C','CheLiangLeiXing':'轿车'}],'ND601RenYuanXinXi':[{'XingMing':'张三','JiGuan':'山东','XueLi':'本科'},{'XingMing':'李四','JiGuan':'北京','XueLi':'硕士'}]}"
        ///  ND601CheLiangXinXi: 从表的ID. 其他的按照顺序与字段对应.
        /// </param>
        /// <param name="athsJSON">附件数据: 格式Demo:"{'FuJian1':[{'FileName':'wx01.png','FileUrl':'http://ccflow.org/image/wx01.png'},{'FileName':'wx02.png','FileUrl':'http://ccflow.org/image/wx02.png'}],'FuJian2':[{'FileName':'ccbpm.css','FileUrl':'http://localhost:2296/DataUser/InstancePacketOfData/Template/ccbpm.css'}]}" .
        /// FuJian1: 标识附件的ID.
        /// </param>
        /// <returns>返回执行的结果.</returns>
        [HttpGet, HttpPost]
        public object Bill_Save(string token, Int64 workID, string bodyData, string dtlsJSON = null, string athsJSON = null)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                DataSet dsDtls = null;
                if (dtlsJSON != null)
                    dsDtls = BP.Tools.Json.ToDataSet(dtlsJSON);

                DataSet dsAths = null;
                if (athsJSON != null)
                    dsAths = BP.Tools.Json.ToDataSet(athsJSON);

                BP.CCBill.Dev2Interface.Bill_Save(workID, bodyData, dsDtls, dsAths);

                ////保存主表数据.
                //string keyVals = "@Tel=18660153393@Addr=山东济南";
                ////从表数据:支持多个从表.通过json的标记与从表的ID对应,标识数据存储到那个从表组件里.
                //string dtlJSON = "{'ND601CheLiangXinXi':[{'ChePaiHao':'黑A888C','CheLiangLeiXing':'轿车'}],'ND601RenYuanXinXi':[{'XingMing':'张三','JiGuan':'山东','XueLi':'本科'},{'XingMing':'李四','JiGuan':'北京','XueLi':'硕士'}]}";
                //// 附件数据:支持多个附件,通过json的标记与附件的ID对应,标识数据存储到那个附件组件里.
                //string athJSON = ;
                ////执行数据保存.
                //Int64 workID = BP.CCBill.Dev2Interface.CreateBlankBillID(frmID);
                //  BP.WF.Dev2Interface.Node_SaveWork
                // BP.CCBill.Dev2Interface.SaveBillWork(frmID);

                return Return_Info(200, "执行成功", workID.ToString());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 预置审批人：
        /// 1. 等开始节点的填写人提交审核的时候，系统就会按照这些预置的审批人进行启动审核。
        /// 2. 设置的审核人是按照顺序审批。
        /// 3. 当前单据的审批模式必须是按照外部的程序调用模式。
        /// </summary>
        /// <param name="token">token</param>
        /// <param name="workID">单据实例的ID</param>
        /// <param name="checkEmpNos">要预置的审核人员，比如:zhangsan,lisi,wangwu 多个人员用逗号分开.</param>
        /// <returns>返回执行结果</returns>
        [HttpGet, HttpPost]
        public object Bill_PreplaceChecker(string token, Int64 workID, string checkEmpNos)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                string info = BP.CCBill.Dev2Interface.Bill_PreplaceChecker(workID, checkEmpNos);
                return Return_Info(200, "执行成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 提交单据：由编辑状态转为归档状态,归档后表单只读.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workID"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Bill_SubmitWork(string token, Int64 workID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                string info = BP.CCBill.Dev2Interface.SubmitWork(workID);
                return Return_Info(200, "执行成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 撤销提交: 由单据的归档状态转变为可编辑.
        /// </summary>
        /// <param name="token">token</param>
        /// <param name="workID">单据实例</param>
        /// <returns>执行结果，撤销成功或者撤销失败.</returns>
        [HttpGet, HttpPost]
        public object Bill_UnSubmitWork(string token, Int64 workID)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                string info = BP.CCBill.Dev2Interface.UnSubmitWork(workID);
                return Return_Info(200, "执行成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 初始化单据的审核人
        /// 1. 如果单据需要审批，审批之前需要初始化审批人.
        /// 2. 执行后，该单据的状态转化为审批中.
        /// </summary>
        /// <param name="token">token</param>
        /// <param name="workID">工作ID</param>
        /// <param name="empNosOfChecker">可以审批的人员,格式:zhangsan,lisi,wangwu</param>
        /// <returns>执行结果</returns>
        [HttpGet, HttpPost]
        public object Bill_CheckerInit(string token, Int64 workID, string empNosOfChecker)
        {
            //根据token登录
            Port_GenerToken(token);
            try
            {
                string info = BP.CCBill.Dev2Interface.Bill_CheckerInit(workID, empNosOfChecker);
                return Return_Info(200, "执行成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        #endregion 单据方法.

        /// <summary>
        /// 企业微信消息推送后台接收
        /// </summary>
        [HttpGet, HttpPost]
        public void QYWX_RECEIVE_API()
        {
            //获取企业号发送的4个参数，验证URL有效性
            string echoString = HttpContextHelper.RequestQueryString("echoStr");//加密的随机字符串
            string signature = HttpContextHelper.RequestQueryString("msg_signature"); //微信加密签名
            string timestamp = HttpContextHelper.RequestQueryString("timestamp");//时间戳
            string nonce = HttpContextHelper.RequestQueryString("nonce");//随机数
            //返回参数，微信企业号验证成功后，自动赋值，如果为空，说明验证失败
            string decryptEchoString = "";
            //配置的token，在设置回调API验证时，随机生成或指定的编号
            string token = BP.Difference.SystemConfig.WX_WeiXinToken;
            string WX_CorpID = BP.Difference.SystemConfig.WX_CorpID;
            string WX_EncodingAESKey = BP.Difference.SystemConfig.WX_EncodingAESKey;
            //开始验证
            if (BP.Port.WeiXin.WXBizMsgCrypt.CheckSignature(token, signature, timestamp, nonce, WX_CorpID, WX_EncodingAESKey, echoString, ref decryptEchoString))
            {
                //不为空，说明验证成功，将参数，返回给企业号
                if (!string.IsNullOrEmpty(decryptEchoString))
                {
                    HttpContextHelper.ResponseWriteString(decryptEchoString, Encoding.UTF8);
                }
            }
        }
        /// <summary>
        /// 执行定时触发
        /// </summary>
        [HttpGet, HttpPost]
        public string DTS_ExecuteTimeTrigger()
        {
            string msg = null;

            try
            {
                ccbpmServices dts3 = new ccbpmServices();
                msg = dts3.Do() as string;
            }
            catch (Exception e)
            {
                BP.DA.Log.DebugWriteError(e);
                return "执行API - DTS_ExecutTimeTrigger出现错误，输出异常的栈信息：\n\r" + e.ToString();
            }

            // 拼接并返回结果字符串，注意处理null值
            StringBuilder result = new StringBuilder("执行完成。<br>");
            if (msg != null)
            {
                result.Append(msg).Append("<br>");
            }
            return result.ToString();
        }

        #region 演示WebApi
        /// <summary>
        /// 省份字典
        /// </summary>
        /// <param name="token">token数据源系统变量</param>
        /// <returns>返回省份数据No,Name的json数据.</returns>
        [HttpGet, HttpPost]
        public object Demo_SFTable_ShengFen(string token)
        {
            //根据token登录.
            try
            {
                Port_GenerToken(token);

                DataTable dt = DBAccess.RunSQLReturnTable("SELECT No,Name FROM Demo_ShengFen ORDER BY No");
                dt.Columns[0].ColumnName = "BianHao";
                dt.Columns[1].ColumnName = "MingCheng"; //这里是故意处理, 用于测试是否可以转化为标准格式的字典.
                return Return_Info(200, "执行成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 根据省份编号获取城市
        /// </summary>
        /// <param name="token">token数据源系统变量</param>
        /// <param name="shengFenNo">省份编号</param>
        /// <returns>返回No,Name 城市json数据.</returns>
        [HttpGet, HttpPost]
        public object Demo_SFTable_City(string token, string shengFenNo)
        {
            //根据token登录.
            try
            {
                Port_GenerToken(token);

                DataTable dt = DBAccess.RunSQLReturnTable("SELECT No as BH,Name as MC FROM Demo_City WHERE ShengFen='" + shengFenNo + "' ORDER BY No");
                dt.Columns[0].ColumnName = "BianHao";
                dt.Columns[1].ColumnName = "MingCheng"; //这里是故意处理, 用于测试是否可以转化为标准格式的字典.
                return Return_Info(200, "执行成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 根据部门编号获的人员
        /// </summary>
        /// <param name="token"></param>
        /// <param name="deptNo">部门编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Demo_SFTable_EmpsByDeptNo(string token, string deptNo)
        {
            //根据token登录.
            try
            {
                Port_GenerToken(token);

                DataTable dt = DBAccess.RunSQLReturnTable("SELECT No,Name FROM Port_Emp WHERE FK_Dept='" + deptNo + "' ORDER BY No");
                dt.Columns[0].ColumnName = "No";
                dt.Columns[1].ColumnName = "Name"; //这里是故意处理, 用于测试是否可以转化为标准格式的字典.
                return Return_Info(200, "执行成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 我部门的人员
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Demo_SFTable_MyDeptEmps(string token)
        {
            //根据token登录.
            try
            {
                Port_GenerToken(token);

                DataTable dt = DBAccess.RunSQLReturnTable("SELECT No,Name FROM Port_Emp WHERE FK_Dept='" + WebUser.DeptNo + "'");
                dt.Columns[0].ColumnName = "No";
                dt.Columns[1].ColumnName = "Name";
                return Return_Info(200, "执行成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        /// <summary>
        /// 我的部门
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Demo_SFTable_MyDepts(string token)
        {
            //根据token登录.
            try
            {
                Port_GenerToken(token);

                DataTable dt = DBAccess.RunSQLReturnTable("SELECT A.No,A.Name FROM Port_Dept A, Port_DeptEmp B WHERE A.No=B.FK_Dept AND B.FK_Emp='" + WebUser.No + "'");
                dt.Columns[0].ColumnName = "No";
                dt.Columns[1].ColumnName = "Name"; //避免大小写问题.
                return Return_Info(200, "执行成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        [HttpGet, HttpPost]
        public object Demo_SFTable_MyStations(string token)
        {
            //根据token登录.
            try
            {
                Port_GenerToken(token);

                DataTable dt = DBAccess.RunSQLReturnTable("SELECT A.No,A.Name FROM Port_Station A, Port_DeptEmpStation B WHERE A.No=B.FK_Station AND B.FK_Emp='" + WebUser.No + "'");
                dt.Columns[0].ColumnName = "No";
                dt.Columns[1].ColumnName = "Name"; //避免大小写问题.
                return Return_Info(200, "执行成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        [HttpGet, HttpPost]
        public object Demo_SFTable_Depts(string token)
        {
            //根据token登录.
            try
            {
                Port_GenerToken(token);

                DataTable dt = DBAccess.RunSQLReturnTable("SELECT No,Name,ParentNo FROM Port_Dept");
                dt.Columns[0].ColumnName = "No";
                dt.Columns[1].ColumnName = "Name"; //避免大小写问题.
                dt.Columns[2].ColumnName = "ParentNo"; //避免大小写问题.
                return Return_Info(200, "执行成功", BP.Tools.Json.ToJson(dt));
            }
            catch (Exception ex)
            {
                return Return_Info(500, "执行失败", ex.Message);
            }
        }
        #endregion 演示WebApi

        #region 数据源API.
        /// <summary>
        /// 获得Search的数据.
        /// </summary>
        /// <param name="token">token</param>
        /// <param name="searchID"></param>
        /// <param name="paras">参数: @key=val@key1=val1</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DBSrc_GenerSearchData(string token, string searchID, string paras)
        {
            //根据token登录.
            Port_GenerToken(token);
            try
            {
                SFSearch sFSearch = new SFSearch(searchID);
                string strs = sFSearch.GenerDataOfJson(paras);
                return Return_Info(200, "执行成功", strs);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        /// <summary>
        /// 查询列表
        /// </summary>
        /// <param name="token"></param>
        /// <param name="searchID"></param>
        /// <param name="paras"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DBSrc_SearchList(string token, string searchID, string paras)
        {
            //根据token登录.
            Port_GenerToken(token);
            try
            {
                SFSearchs ens = new SFSearchs();
                ens.RetrieveAll();

                return Return_Info(200, "执行成功", ens.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        /// <summary>
        /// 获得查询的列集合
        /// </summary>
        /// <param name="token"></param>
        /// <param name="searchID"></param>
        /// <param name="paras"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object DBSrc_SearchCols(string token, string searchID, string paras)
        {
            //根据token登录.
            Port_GenerToken(token);
            try
            {
                SFColumns ens = new SFColumns();
                ens.Retrieve("RefPKVal", searchID);

                return Return_Info(200, "执行成功", ens.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        #endregion 数据源API.


        #region ccfast 菜单处理.
        /// <summary>
        /// 获得菜单信息
        /// </summary>
        /// <param name="token"></param>
        /// <param name="menuMyPK"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object CCFast_MenuInfo(string token, string menuMyPK)
        {
            //根据token登录.
            Port_GenerToken(token);
            try
            {
                Menu en = new Menu();
                en.No = menuMyPK;
                en.Retrieve();
                return Return_Info(200, "执行成功", en.ToJson());
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        /// <summary>
        /// 菜单删除
        /// </summary>
        /// <param name="token"></param>
        /// <param name="menuMyPK"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object CCFast_MenuDelete(string token, string menuMyPK)
        {
            Port_GenerToken(token);//根据token登录.
            try
            {
                Menu en = new Menu();
                en.No = menuMyPK;
                en.Delete();
                return Return_Info(200, "执行成功", "删除成功.");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        /// <summary>
        /// 获得最后一个菜单.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="menuMyPK"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object CCFast_LastOneMenu(string token, string menuMyPK)
        {
            Port_GenerToken(token);//根据token登录.
            try
            {
                string sql = "SELECT No,Name FROM GPM_Menu WHERE MenuModel='" + menuMyPK + "' ORDER BY RDT DESC ";
                DataTable dt = DBAccess.RunSQLReturnTable(sql);
                if (dt.Rows.Count == 0)
                    return Return_Info(200, "执行成功", "");

                string no = dt.Rows[0][0].ToString();
                string name = dt.Rows[0][1].ToString();

                string info = "@MenuNo=" + no + "@MenuName=" + name;
                return Return_Info(200, "执行成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }

        /// <summary>
        /// 获得系统信息
        /// </summary>
        /// <param name="token">token</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object CCFast_LastOneSystem(string token)
        {
            Port_GenerToken(token);//根据token登录.
            try
            {
                string sql = "SELECT No,Name FROM GPM_System WHERE 1=1 ORDER BY RDT DESC ";
                DataTable dt = DBAccess.RunSQLReturnTable(sql);
                if (dt.Rows.Count == 0)
                    return Return_Info(200, "执行成功", "");

                string no = dt.Rows[0][0].ToString();
                string name = dt.Rows[0][1].ToString();

                string info = "@No=" + no + "@Name=" + name;
                return Return_Info(200, "执行成功", info);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        /// <summary>
        /// 获得系统信息
        /// </summary>
        /// <param name="token"></param>
        /// <param name="systemNo">系统编号</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object CCFast_GenerOneSystem(string token, string systemNo)
        {
            Port_GenerToken(token);//根据token登录.
            try
            {
                DataSet ds = new DataSet();

                MySystem system = new MySystem(systemNo);
                ds.Tables.Add(system.ToDataTableField("System"));

                BP.CCFast.CCMenu.Modules mds = new Modules();
                mds.Retrieve("SystemNo", systemNo, "Idx");
                ds.Tables.Add(mds.ToDataTableField("Modules"));

                BP.CCFast.CCMenu.Menus menus = new Menus();
                menus.Retrieve("SystemNo", systemNo, "Idx");
                ds.Tables.Add(menus.ToDataTableField("Menus"));

                string json = BP.Tools.Json.ToJson(ds);
                return Return_Info(200, "执行成功", json);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }

        }
        /// <summary>
        /// 检查菜单基础数据是否完整
        /// </summary>
        /// <param name="token"></param>
        /// <param name="appNo"></param>
        /// <param name="appName"></param>
        /// <param name="sortNo"></param>
        /// <param name="sortName"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object CCFast_Mouldle_Init(string token, string appNo, string appName, string sortNo, string sortName)
        {
            Port_GenerToken(token);//根据token登录.
            try
            {
                BP.CCFast.CCMenu.Module mu = new Module();
                mu.No = sortNo;
                if (mu.RetrieveFromDBSources() == 1)
                    return Return_Info(200, "执行成功", "数据正常.");

                MySystem system = new MySystem();
                system.No = sortName;
                if (system.RetrieveFromDBSources() == 0)
                {
                    system.Name = appName;
                    system.Insert();
                }

                mu.No = sortNo;
                mu.Name = sortName;
                mu.SystemNo = appNo;
                mu.Insert();
                return Return_Info(200, "执行成功", "创建成功，数据正常.");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        #endregion 菜单处理.


        #region 错误异常处理.
        public class ErrBody
        {
            public string title { get; set; }
            public string docs { get; set; }
            public string IPAddr { get; set; }
            public string Locatinon { get; set; }
            public string Brower { get; set; }
            public string OS { get; set; }
   
        }
        [HttpPost]
        public string DealErrInfo([FromBody] ErrBody body)
        {
            try
            {
                string title = body.title;
                string docs = body.docs;
                string IPAddr = body.IPAddr;
                string Locatinon = body.Locatinon;
                string Brower = body.Brower;
                string OS = body.OS;


                string timeKey = DateTime.Now.ToString("MMddmmDDss");

                string data = DateTime.Now.ToString("yyyy-MM-dd");
                string dir = SystemConfig.PathOfDataUser + "\\Log\\" + data + "\\";
                if (System.IO.Directory.Exists(dir) == false)
                    Directory.CreateDirectory(dir);
                string tempfile = dir + timeKey + "-" + DBAccess.GenerGUID() + ".txt";

                string sql = "INSERT INTO sys_bug_Log (MyPK,Title,Content,IPAddr,Locatinon,Brower,OS,CreateTime) VALUES ('" + DBAccess.GenerGUID() + "','" + title + "','" + docs +  "','" + IPAddr + "','" + Locatinon + "','" + Brower +  "','" + OS + "','"+ DataType.CurrentDateTimess + "')";
                DBAccess.RunSQL(sql);

                //DataType.WriteFile(tempfile, title + docs);
                return "写入成功.";
            }
            catch
            {
                return "写入失败.";
            }
        }
        #endregion

        #region 
        public class WorkBody
        {
            public long workID { get; set; }
            public string keyVals { get; set; }
            public string dtlJSON { get; set; }
            public string athJSON { get; set; }

            public string token { get; set; }
            public string toNodeIDStr { get; set; }
            public string toEmps { get; set; }
            public string paras { get; set; }
            public string checkNote { get; set; }
        }
        /// <summary>
        /// 保存节点表单数据：参数以body形式传入
        /// </summary>
        /// <param name="token"></param>
        /// <param name="workbody">表单数据集合：
        /// workID:工作id，
        /// keyVals：要保存的主表数据，格式： @Key1=Val2@Key2=Val2  @Em=zhangsan@ccflow.org@Tel=18660153393，
        /// dtlJSON：从表数据,格式： 多个从表的表名按照与从表控件的ID对应，字段与控件的字段对应。
        /// athJSON：附件数据,格式： 多个附件: 格式 FileName, FileUrl。一定是 FileName 在前，FileUrl 在后。格式： {'Ath1':[{'FileName':'我的附件1.doc','FileUrl':'http://localst:9003:/xxx.docx'},{'FileName':'我的附件2.doc','FileUrl':'http://localst:9003:/xxx.docx'}]} </param>
        /// <returns>执行结果</returns>
        [HttpGet, HttpPost]
        public object Node_SaveWorkByMap([FromBody] WorkBody workbody)
        {
            //根据token登录
            Port_GenerToken(workbody.token);
            try
            {
                AtPara ap = new AtPara(workbody.keyVals);

                DataSet dsDtls = null;
                if (!DataType.IsNullOrEmpty(workbody.dtlJSON))
                    dsDtls = BP.Tools.Json.ToDataSet(workbody.athJSON);

                DataSet dsAths = null;
                if (!DataType.IsNullOrEmpty(workbody.athJSON))
                    dsAths = BP.Tools.Json.ToDataSet(workbody.athJSON);

                //执行保存方法.
                BP.WF.Dev2Interface.Node_SaveWork(workbody.workID, ap.HisHT, dsDtls, dsAths);

                return Return_Info(200, "表单主表数据,执行成功.", "");
            }
            catch (Exception ex)
            {
                return Return_Info(500, "工作保存失败", ex.Message);
            }
        }
        /// <summary>
        /// 发送流程：参数以body形式传入
        /// </summary>
        /// <param name="workbody">保存需要的参数：token:token
        /// workID：工作实例WorkID
        /// toNodeIDStr:到达的下一个节点,默认为0,可以是节点Mark. 
        /// toEmps:下一个节点的接收人，多个人用逗号分开比如:zhangsan,lisi
        /// paras:参数，保存到WF_GenerWorkFlow,用与参数条件,格式: @key1=val1@Key2=Val2
        /// checkNote:审核意见启用了审核组件，就需要填写审核意见,负责不让发送。</param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Node_SendWorkByMap([FromBody] WorkBody workbody)
        {
            try
            {
                //根据token登录
                Port_GenerToken(workbody.token);

                //保存参数.
                if (DataType.IsNullOrEmpty(workbody.paras) == false)
                    Dev2Interface.Flow_SaveParas(workbody.workID, workbody.paras);

                //写入审核意见.
                if (DataType.IsNullOrEmpty(workbody.checkNote) == false)
                    Dev2Interface.Node_WriteWorkCheck(workbody.workID, workbody.checkNote, null, null, null);

                string flowNo = DBAccess.RunSQLReturnString("SELECT FK_Flow FROM WF_GenerWorkFlow WHERE WorkID=" + workbody.workID);
                int toNodeID = DealNodeIDStr(workbody.toNodeIDStr, flowNo);

                //执行发送.
                SendReturnObjs objs = Dev2Interface.Node_SendWork(flowNo, workbody.workID, null, null, toNodeID, workbody.toEmps);

                string msg = objs.ToMsgOfText();

                //保存参数到Rpt表.
                if (!DataType.IsNullOrEmpty(workbody.paras))
                {
                    Flow flow = new Flow();
                    flow.No = flowNo;
                    if (flow.RetrieveFromDBSources() > 0)
                    {
                        string sql = "UPDATE " + flow.PTable + " SET AtPara = '" + workbody.paras + "' WHERE OID = " + workbody.workID;
                        DBAccess.RunSQL(sql);
                    }
                }
                return Return_Info(200, "发送成功", msg);
            }
            catch (Exception ex)
            {
                return Return_Info(500, "发送失败", ex.Message);
            }

        }
        #endregion


        //[HttpGet]
        //public async Task<ActionResult> GetGiteeCode(string client_id, string redirect_uri)
        //{
        //    var client = _httpClientFactory.CreateClient();
        //    var url = "https://gitee.com/oauth/authorize?client_id=" + client_id + "&redirect_uri=" + redirect_uri + "&response_type=code";
        //    var response = await client.GetAsync(url);
        //    response.EnsureSuccessStatusCode();
        //    // 读取响应内容为字符串
        //    var content = await response.Content.ReadAsStringAsync();
        //    // 返回 200 OK 响应，并带上响应内容
        //    return Ok(content);
        //}


        [HttpPost]
        public async Task<ActionResult> GetGiteeToken([FromForm] string grant_type, [FromForm] string code, [FromForm] string client_id, [FromForm] string redirect_uri, [FromForm] string client_secret)
        {
            var client = _httpClientFactory.CreateClient();
            var url = "https://gitee.com/oauth/token";

            object parameters = new
            {
                grant_type,
                code,
                client_id,
                redirect_uri,
                client_secret
            };
            string json = System.Text.Json.JsonSerializer.Serialize(parameters);
            HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
            var response = await client.PostAsync(url, content);
            // 读取响应内容
            string responseBody = await response.Content.ReadAsStringAsync();

            return Ok(responseBody);
        }

        [HttpPost]
        public async Task<ActionResult> CheckGiteeStarred([FromForm] string owner_name, [FromForm] string repo_name, [FromForm] string access_token)
        {
            var client = _httpClientFactory.CreateClient();
            client.DefaultRequestHeaders.Add("Authorization", "Bearer " + access_token);
            var url = "https://gitee.com/api/v5/user/starred/" + owner_name + "/" + repo_name;
            var response = await client.GetAsync(url);
            return Ok(response);
        }
        #region 获得流程的节点信息.
        /// <summary>
        /// 获得流程的节点信息
        /// </summary>
        /// <param name="token"></param>
        /// <param name="flowNo"></param>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public object Flow_Nodes_Info(string token, string flowNo)
        {
            //根据token登录.
            Port_GenerToken(token);
            try
            {
                if (DataType.IsNullOrEmpty(flowNo))
                {
                    return "err@参数错误！";
                }
                BP.WF.Nodes nodes = new BP.WF.Nodes();
                nodes.Retrieve("FK_Flow", flowNo);
                //因直接使用nodes.ToJson()无法获取某些字段（e.g.HisFormTypeText,原因：Node没有自己的Attr类）
                //故此处手动创建前台所需的DataTable
                DataTable dt = new DataTable();
                dt.Columns.Add("NodeID");   //节点ID
                dt.Columns.Add("Name");     //节点名称
                dt.Columns.Add("HisFormType");      //表单方案
                dt.Columns.Add("HisFormTypeText");
                dt.Columns.Add("HisRunModel");      //节点类型
                dt.Columns.Add("HisRunModelT");

                dt.Columns.Add("HisDeliveryWay");   //接收方类型
                dt.Columns.Add("HisDeliveryWayText");
                dt.Columns.Add("HisDeliveryWayJsFnPara");
                dt.Columns.Add("HisDeliveryWayCountLabel");
                dt.Columns.Add("HisDeliveryWayCount");  //接收方Count

                dt.Columns.Add("HisCCRole");    //抄送人
                dt.Columns.Add("HisCCRoleText");
                dt.Columns.Add("HisFrmEventsCount");    //消息&事件Count
                dt.Columns.Add("HisFinishCondsCount");  //流程完成条件Count
                DataRow dr;
                foreach (BP.WF.Node node in nodes)
                {
                    dr = dt.NewRow();
                    dr["NodeID"] = node.NodeID;
                    dr["Name"] = node.Name;
                    dr["HisFormType"] = node.HisFormType;
                    dr["HisFormTypeText"] = node.HisFormTypeText;
                    dr["HisRunModel"] = node.HisRunModel;
                    dr["HisRunModelT"] = node.HisRunModelT;
                    dr["HisDeliveryWay"] = node.HisDeliveryWay;
                    dr["HisDeliveryWayText"] = node.HisDeliveryWayText;

                    //接收方数量
                    int intHisDeliveryWayCount = 0;
                    if (node.HisDeliveryWay == BP.WF.DeliveryWay.ByStation)
                    {
                        dr["HisDeliveryWayJsFnPara"] = "ByStation";
                        dr["HisDeliveryWayCountLabel"] = "角色";
                        BP.WF.Template.NodeStations nss = new BP.WF.Template.NodeStations();
                        intHisDeliveryWayCount = nss.Retrieve(BP.WF.Template.NodeStationAttr.FK_Node, node.NodeID);
                    }
                    else if (node.HisDeliveryWay == BP.WF.DeliveryWay.ByDept)
                    {
                        dr["HisDeliveryWayJsFnPara"] = "ByDept";
                        dr["HisDeliveryWayCountLabel"] = "部门";
                        BP.WF.Template.NodeDepts nss = new BP.WF.Template.NodeDepts();
                        intHisDeliveryWayCount = nss.Retrieve(BP.WF.Template.NodeDeptAttr.FK_Node, node.NodeID);
                    }
                    else if (node.HisDeliveryWay == BP.WF.DeliveryWay.ByBindEmp)
                    {
                        dr["HisDeliveryWayJsFnPara"] = "ByDept";
                        dr["HisDeliveryWayCountLabel"] = "人员";
                        BP.WF.Template.NodeEmps nes = new BP.WF.Template.NodeEmps();
                        intHisDeliveryWayCount = nes.Retrieve(BP.WF.Template.NodeStationAttr.FK_Node, node.NodeID);
                    }
                    dr["HisDeliveryWayCount"] = intHisDeliveryWayCount;

                    //抄送
                    dr["HisCCRole"] = node.HisCCRole;
                    dr["HisCCRoleText"] = node.HisCCRoleText;

                    //消息&事件Count
                    BP.Sys.FrmEvents fes = new BP.Sys.FrmEvents();
                    dr["HisFrmEventsCount"] = fes.Retrieve(BP.Sys.FrmEventAttr.FrmID, "ND" + node.NodeID);

                    //流程完成条件Count
                    BP.WF.Template.Conds conds = new BP.WF.Template.Conds(BP.WF.Template.CondType.Flow, node.NodeID);
                    dr["HisFinishCondsCount"] = conds.Count;

                    dt.Rows.Add(dr);
                }
                return BP.Tools.Json.ToJson(dt);

            }
            catch (Exception ex)
            {
                return Return_Info(500, "失败", ex.Message);
            }
        }
        #endregion
    }
}